feat(glue): Add hashed_mirrors support to eval fetcher

This change adds basic scaffolding to allow configuring hashed_mirrors that will be used
by fetchurl to download artifacts by their hash, this is useful in case certain URLs are
no longer available but required to bootstrap nixpkgs stdenv.

These urls will have higher priority than the url specified in fetchurl(and friends) and
will be attempted before falling back to the actual url specified in fetchurl.

Change-Id: I589bdef609075f274cbdf6b26af602cafaa7496a
Reviewed-on: https://cl.snix.dev/c/snix/+/30567
Tested-by: besadii
Reviewed-by: Florian Klink <flokli@flokli.de>
This commit is contained in:
Vova Kryachko 2025-06-11 04:59:50 -04:00
parent 3c23b323d5
commit d741ca4bb1
10 changed files with 25 additions and 0 deletions

1
snix/Cargo.lock generated
View file

@ -4294,6 +4294,7 @@ dependencies = [
"tokio", "tokio",
"tracing", "tracing",
"tracing-indicatif", "tracing-indicatif",
"url",
"wu-manber", "wu-manber",
] ]

View file

@ -14114,6 +14114,10 @@ rec {
name = "tracing-indicatif"; name = "tracing-indicatif";
packageId = "tracing-indicatif"; packageId = "tracing-indicatif";
} }
{
name = "url";
packageId = "url";
}
{ {
name = "wu-manber"; name = "wu-manber";
packageId = "wu-manber"; packageId = "wu-manber";

View file

@ -27,6 +27,7 @@ tracing-indicatif.workspace = true
rustc-hash.workspace = true rustc-hash.workspace = true
mimalloc.workspace = true mimalloc.workspace = true
wu-manber.workspace = true wu-manber.workspace = true
url.workspace = true
[dev-dependencies] [dev-dependencies]
expect-test.workspace = true expect-test.workspace = true

View file

@ -2,6 +2,7 @@ use std::path::PathBuf;
use clap::Parser; use clap::Parser;
use snix_store::utils::ServiceUrlsMemory; use snix_store::utils::ServiceUrlsMemory;
use url::Url;
/// Provides a CLI interface to trigger evaluation using snix-eval. /// Provides a CLI interface to trigger evaluation using snix-eval.
/// ///
@ -79,6 +80,13 @@ pub struct Args {
/// Snix does not read from these. /// Snix does not read from these.
#[clap(long)] #[clap(long)]
pub drv_dumpdir: Option<PathBuf>, pub drv_dumpdir: Option<PathBuf>,
/// A list of web servers used by builtins.fetchurl to obtain files by hash.
/// Given a hash algorithm ha and a base-16 hash h, Nix will try to download the file
/// from hashed-mirror/ha/h. This allows files to be downloaded even if they have
/// disappeared from their original URI.
#[clap(long, default_values_t = [Url::parse("https://tarballs.nixos.org/").unwrap()])]
pub hashed_mirrors: Vec<Url>,
} }
impl Args { impl Args {

View file

@ -56,6 +56,7 @@ pub fn init_io_handle(tokio_runtime: &tokio::runtime::Runtime, args: &Args) -> R
nar_calculation_service.into(), nar_calculation_service.into(),
build_service.into(), build_service.into(),
tokio_runtime.handle().clone(), tokio_runtime.handle().clone(),
args.hashed_mirrors.clone(),
)) ))
} }

View file

@ -38,6 +38,7 @@ fn interpret(code: &str) {
nar_calculation_service.into(), nar_calculation_service.into(),
Arc::<DummyBuildService>::default(), Arc::<DummyBuildService>::default(),
TOKIO_RUNTIME.handle().clone(), TOKIO_RUNTIME.handle().clone(),
Vec::new(),
)); ));
let mut eval_builder = snix_eval::Evaluation::builder(Box::new(SnixIO::new( let mut eval_builder = snix_eval::Evaluation::builder(Box::new(SnixIO::new(

View file

@ -91,6 +91,7 @@ mod tests {
nar_calculation_service.into(), nar_calculation_service.into(),
Arc::<DummyBuildService>::default(), Arc::<DummyBuildService>::default(),
runtime.handle().clone(), runtime.handle().clone(),
Vec::new(),
)); ));
let mut eval_builder = snix_eval::Evaluation::builder(io.clone() as Rc<dyn EvalIO>); let mut eval_builder = snix_eval::Evaluation::builder(io.clone() as Rc<dyn EvalIO>);

View file

@ -176,6 +176,7 @@ pub struct Fetcher<BS, DS, PS, NS> {
directory_service: DS, directory_service: DS,
path_info_service: PS, path_info_service: PS,
nar_calculation_service: NS, nar_calculation_service: NS,
_hashed_mirrors: Vec<Url>,
} }
impl<BS, DS, PS, NS> Fetcher<BS, DS, PS, NS> { impl<BS, DS, PS, NS> Fetcher<BS, DS, PS, NS> {
@ -184,6 +185,7 @@ impl<BS, DS, PS, NS> Fetcher<BS, DS, PS, NS> {
directory_service: DS, directory_service: DS,
path_info_service: PS, path_info_service: PS,
nar_calculation_service: NS, nar_calculation_service: NS,
hashed_mirrors: Vec<Url>,
) -> Self { ) -> Self {
Self { Self {
http_client: reqwest::Client::builder() http_client: reqwest::Client::builder()
@ -194,6 +196,7 @@ impl<BS, DS, PS, NS> Fetcher<BS, DS, PS, NS> {
directory_service, directory_service,
path_info_service, path_info_service,
nar_calculation_service, nar_calculation_service,
_hashed_mirrors: hashed_mirrors,
} }
} }

View file

@ -13,6 +13,7 @@ use std::{
use tokio_util::io::SyncIoBridge; use tokio_util::io::SyncIoBridge;
use tracing::{Level, Span, error, instrument, warn}; use tracing::{Level, Span, error, instrument, warn};
use tracing_indicatif::span_ext::IndicatifSpanExt; use tracing_indicatif::span_ext::IndicatifSpanExt;
use url::Url;
use snix_castore::{ use snix_castore::{
Node, Node,
@ -72,6 +73,7 @@ impl SnixStoreIO {
nar_calculation_service: Arc<dyn NarCalculationService>, nar_calculation_service: Arc<dyn NarCalculationService>,
build_service: Arc<dyn BuildService>, build_service: Arc<dyn BuildService>,
tokio_handle: tokio::runtime::Handle, tokio_handle: tokio::runtime::Handle,
hashed_mirrors: Vec<Url>,
) -> Self { ) -> Self {
Self { Self {
blob_service: blob_service.clone(), blob_service: blob_service.clone(),
@ -86,6 +88,7 @@ impl SnixStoreIO {
directory_service, directory_service,
path_info_service, path_info_service,
nar_calculation_service, nar_calculation_service,
hashed_mirrors,
), ),
known_paths: Default::default(), known_paths: Default::default(),
} }
@ -516,6 +519,7 @@ mod tests {
nar_calculation_service.into(), nar_calculation_service.into(),
Arc::<DummyBuildService>::default(), Arc::<DummyBuildService>::default(),
tokio_runtime.handle().clone(), tokio_runtime.handle().clone(),
Vec::new(),
)); ));
let mut eval_builder = let mut eval_builder =

View file

@ -48,6 +48,7 @@ fn eval_test(code_path: PathBuf, expect_success: bool) {
nar_calculation_service.into(), nar_calculation_service.into(),
Arc::new(DummyBuildService::default()), Arc::new(DummyBuildService::default()),
tokio_runtime.handle().clone(), tokio_runtime.handle().clone(),
Vec::new(),
)); ));
// Wrap with SnixIO, so <nix/fetchurl.nix can be imported. // Wrap with SnixIO, so <nix/fetchurl.nix can be imported.
let mut eval_builder = snix_eval::Evaluation::builder(Box::new(SnixIO::new( let mut eval_builder = snix_eval::Evaluation::builder(Box::new(SnixIO::new(