Recursive registry vendoring doesn't work
My crate foo has a dependency on crate bar from custom registry registry1, and bar is itself dependent on crate baz from registry2. registry1 and registry2 are in fact two different URLs for the same registry, one of them is sparse+http:// and the other one is git:// (but this doesn't matter in the general case).
Even if I configure Crane with both registries, it only vendors crates from registry1. Then it tries to access registry2 during cargo check in another derivation, which of course fails due to network access issues.
How hard do you think it would be to implement proper recursive vendoring?
Is it possible to temporarily work around this issue without modifying the crates in the custom registry?
I tried to put the following config into .cargo/config.toml:
# registry used to download `bar`
[registries.registry1]
index = "sparse+http://example.com/api/v1/crates/"
# the registry that `bar` wants `baz` from
[source.replacement2]
registry = "git://example.com/index"
replace-with = "registry1"
It fixed the issue of trying to access registry2 (git://example.com/index) from sandbox, but it was causing errors like this:
error[E0308]: mismatched types
--> /nix/store/8krp2f7953dmzlg6ds096dafnhyh1yga-vendor-cargo-deps/059aab26819e2c754aab8ea711140537883f6c4c885fb914bc87eaf4194b5795/bar-0.2.3/src/watermelon.rs:454:21
|
454 | redacted,
| ^^^^^^^^ expected `FooBarType<u8>`, found a different `FooBarType<u8>`
|
= note: `FooBarType<u8>` and `FooBarType<u8>` have similar names, but are actually distinct types
note: `FooBarType<u8>` is defined in crate `baz`
--> /nix/store/8krp2f7953dmzlg6ds096dafnhyh1yga-vendor-cargo-deps/059aab26819e2c754aab8ea711140537883f6c4c885fb914bc87eaf4194b5795/baz-0.1.1/src/banana.rs:32:1
|
32 | pub struct FooBarType<T>(Box<[T]>);
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: `FooBarType<u8>` is defined in crate `baz`
--> /nix/store/8krp2f7953dmzlg6ds096dafnhyh1yga-vendor-cargo-deps/059aab26819e2c754aab8ea711140537883f6c4c885fb914bc87eaf4194b5795/baz-0.1.1/src/banana.rs:32:1
|
32 | pub struct FooBarType<T>(Box<[T]>);
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: perhaps two different versions of crate `baz` are being used?
and looking at the output of cargo check we see that it was indeed working with two identical copies of baz (from both sources, most likely because: foo -> bar(registry1) -> baz(registry2), foo -> baz(registry1), foo also depends on baz):
Checking baz v0.1.1 (registry `replacement2`)
Checking baz v0.1.1 (registry `registry1`)
Note that it works fine when running cargo check with the modified .cargo/config.toml outside of Crane:
Checking baz v0.1.1 (registry `registry1`)
Hi @ondt thanks for the report! I unfortunately do not have access to multiple registries that I can test with so I may need to rely on you to help me debug this
The vendoring process has two steps:
- Download all referenced packages and put them in the Nix store, then build a symlink farm for each registry/git repo
- Append some configuration (in the equivalent of
$HOME/.cargo/config.tomlto avoid touching the project's files directly) which would instruct cargo to use the vendored directories instead of trying to fetch things directly
As long as the Cargo.lock is fully up to date, 1. should complete without issues (since we crawl the entire contents of Cargo.lock); IIRC cargo should be committing any source overrides in the Cargo.lock but in case it isn't we can dig into it further
As for 2. i wonder if mixing source replacements with registries (and source vendoring) is running into a precedence issue or something else. My first advice is to try running nix build .#whatever --keep-failed and then analyzing the left-over directory; e.g. do the configs look correct, are all the packages present, and so on
Hello @ipetkov, sorry for the delayed response.
I unfortunately do not have access to multiple registries
You can test it with just one registry.
All that is needed is the following setup:
- Binary
foothat you're building with Crane - Libraries
barandbazlocated on a registrysparse+http://your.registry.com/api/v1/crates/(we're usingKellnrbut it shouldn't matter)-
baris dependent onbaz -
foois dependent onbar
-
- Registry URL configured using
registryFromSparse, and also configured infoo's.cargo/config.toml
In this setup the build of foo should work. However, if you change the URL used to access the registry when building foo (in .cargo/config.toml) to sparse+http://your.registry.com:80/api/v1/crates/ (explicitly adding port 80 or 443 or using a different domain name alias), the build fails.
Now we've got two distinct URLs for the same registry, let's call them url1 and url2:
-
url1:sparse+http://your.registry.com/api/v1/crates/ -
url2:sparse+http://your.registry.com:80/api/v1/crates/
After changing the URL the situation is as follows:
- Binary
foorequiresbarthroughurl2 -
barstill requiresbazthroughurl1;url1was baked intobarat the time of publishing
Now, even if we configure both url1 and url2 using registryFromSparse, then Crane still fails to run subsequent cargo commands, because cargo would be trying to access url1 from the sandbox, and I believe that's because baz wasn't vendored at all, or it was vendored improperly.
:warning: Note that after changing url1 to url2 in foo's .cargo/config.toml, the foo's Cargo.lock ONLY contains url2, but cargo still accesses url1 when downloading baz. That's why I believe Crane didn't know about url1, and therefore didn't vendor baz.
I believe this issue is caused in step 1 as you described it. As for step 2 and source replacements, I suggest not to focus on it right now, it's possible that it would fix itself if we manage to fix step 1. If not, then we should open a separate issue for it.
My initial description of the issue was very condensed and kind of all over the place, sorry about that!
By the way, the example with port 80 is just for simplicity. We actually encountered this issue while transitioning from git to sparse. ALL the libraries in the registry must be updated to use the new sparse URL. The Crane build breaks even if just one of them uses the old git URL.
This issue also applies to scenarios with two actually separate registries, but that is not our case. (However, in this case it can't be mitigated by just by publishing all the crates again with fixed URLs, projects like these can't be built at all using Crane.)
@ondt thanks for the detailed write up!
Is it possible to condense those instructions into a flake that I could check out and repro directly? Alexandrie is an open registry I've used for testing before, but it can be any registry that works!
I ran into the same issue. It works fine with a cargo build outside of Nix. But inside Nix the transient dependency's registry is never referenced in the crane generated config.toml, unless it's explicitly added to the app's .cargo/config.toml. Unfortunately I can't share our private registry setup, but I'll see if I can reproduce something with the public repos.
@ipetkov I prepared a repro case over at https://github.com/szlend/crane-reg-test
The cargo build in the devshell works fine, but nix build fails when the cargo check tries to fetch crates from the transitive custom registry.
> ++ command cargo check --release --locked --all-targets
> Updating `https://github.com/szlend/alexandrie-index` index
> warning: spurious network error (3 tries remaining): [35] SSL connect error (OpenSSL/3.0.14: error:16000069:STORE routines::unregistered scheme)
> warning: spurious network error (2 tries remaining): [35] SSL connect error (OpenSSL/3.0.14: error:16000069:STORE routines::unregistered scheme)
> warning: spurious network error (1 tries remaining): [35] SSL connect error (OpenSSL/3.0.14: error:16000069:STORE routines::unregistered scheme)
> error: failed to download from `https://crates.polomack.eu/api/v1/crates/epitech_api/0.2.0/download`
crane-reg-test-deps> will append /private/tmp/nix-build-crane-reg-test-deps-0.1.0.drv-0/source/.cargo-home/config.toml with contents of /nix/store/4w5wg6zc4xzlwcqn5dwqcg7k5mb3hkxn-vendor-cargo-deps/config.toml
[source.nix-sources-8ffc52aa4d04993f2f8485e30b54e0763e4429374c2a679592e85187efa925b5]
directory = "/nix/store/4w5wg6zc4xzlwcqn5dwqcg7k5mb3hkxn-vendor-cargo-deps/8ffc52aa4d04993f2f8485e30b54e0763e4429374c2a679592e85187efa925b5"
[source.nix-sources-c19b7c6f923b580ac259164a89f2577984ad5ab09ee9d583b888f934adbbe8d0]
directory = "/nix/store/4w5wg6zc4xzlwcqn5dwqcg7k5mb3hkxn-vendor-cargo-deps/c19b7c6f923b580ac259164a89f2577984ad5ab09ee9d583b888f934adbbe8d0"
[source.nix-sources-f7e93db4c478e311b0c8ac8822989922129c8770f333062d10356eb5b8bdaf04]
directory = "/nix/store/4w5wg6zc4xzlwcqn5dwqcg7k5mb3hkxn-vendor-cargo-deps/f7e93db4c478e311b0c8ac8822989922129c8770f333062d10356eb5b8bdaf04"
[source.alexandrie]
registry = "https://github.com/Hirevo/alexandrie-index"
replace-with = "nix-sources-f7e93db4c478e311b0c8ac8822989922129c8770f333062d10356eb5b8bdaf04"
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = "nix-sources-c19b7c6f923b580ac259164a89f2577984ad5ab09ee9d583b888f934adbbe8d0"
Note that there should be two registries in the dependency chain:
- https://github.com/Hirevo/alexandrie-index
- https://github.com/szlend/alexandrie-index
Only one of them gets put into crane's generated .cargo/config.toml. A workaround is to list all transitive registries in the root project that you're building. But this isn't necessary with plain cargo (without crane).
Another thing to note. Registries from transitive deps are unnamed when building:
Compiling epitech_api v0.2.0 (registry `https://github.com/szlend/alexandrie-index`) <---- unnamed
Compiling crane-test-dep v0.2.0 (registry `alexandrie`)
My attempt at fixing this is in #676