@@ -330,6 +330,34 @@ pub struct InstallSummary {
330330 pub packages : Vec < PackageId > ,
331331}
332332
333+ /// Whether a package's `post-link` / `pre-unlink` scripts run when it is linked
334+ /// into a prefix.
335+ ///
336+ /// Skipped by default. Those scripts are arbitrary code shipped inside a
337+ /// package and run with the installer's privileges, and they are what makes an
338+ /// install non-hermetic — they can reach the network or bake host state into
339+ /// the prefix, so a packed bundle stops being reproducible offline. A few
340+ /// packages (some CUDA and MKL builds, older R builds) do real work there, so a
341+ /// caller that trusts the channels it installs from can opt in.
342+ #[ derive( Clone , Copy , Debug , Default , PartialEq , Eq ) ]
343+ pub enum LinkScripts {
344+ /// Link files only; `post-link` / `pre-unlink` scripts are ignored.
345+ #[ default]
346+ Skip ,
347+ /// Execute each package's `post-link` / `pre-unlink` scripts.
348+ Run ,
349+ }
350+
351+ impl From < bool > for LinkScripts {
352+ fn from ( run : bool ) -> Self {
353+ if run {
354+ Self :: Run
355+ } else {
356+ Self :: Skip
357+ }
358+ }
359+ }
360+
333361/// Install the `environment`/`platform` packages from `lock` into `prefix`,
334362/// using rattler's installer (no conda required). Packages are fetched into the
335363/// shared package cache and linked into the prefix.
@@ -340,9 +368,10 @@ pub async fn install_lock(
340368 environment : & str ,
341369 platform : & str ,
342370 prefix : & Path ,
371+ link_scripts : LinkScripts ,
343372) -> Result < InstallSummary , InstallError > {
344373 let records = lock_records ( lock, environment, platform) ?;
345- install_records ( records, environment, platform, prefix) . await
374+ install_records ( records, environment, platform, prefix, link_scripts ) . await
346375}
347376
348377/// Render `error` and its `source` chain as `outer: cause: root cause`.
@@ -445,6 +474,7 @@ pub async fn install_records(
445474 environment : & str ,
446475 platform : & str ,
447476 prefix : & Path ,
477+ link_scripts : LinkScripts ,
448478) -> Result < InstallSummary , InstallError > {
449479 let target = Platform :: from_str ( platform)
450480 . map_err ( |e| InstallError :: Lock ( format ! ( "bad platform '{platform}': {e}" ) ) ) ?;
@@ -459,6 +489,7 @@ pub async fn install_records(
459489 . with_download_client ( crate :: net:: authenticated_client ( ) . map_err ( InstallError :: Install ) ?)
460490 . with_max_concurrent_requests ( MAX_CONCURRENT_FETCHES )
461491 . with_target_platform ( target)
492+ . with_execute_link_scripts ( link_scripts == LinkScripts :: Run )
462493 . install ( prefix, records)
463494 . await
464495 . map_err ( |e| InstallError :: Install ( error_chain ( & e) ) ) ?;
@@ -489,10 +520,18 @@ pub async fn create(
489520 coords : & Coordinates ,
490521 label : & Label ,
491522 prefix : & Path ,
523+ link_scripts : LinkScripts ,
492524) -> Result < InstallSummary , InstallError > {
493525 let bytes = registry. pull ( coords, label) ?;
494526 let lock = parse_lock ( & bytes) ?;
495- let summary = install_lock ( & lock, & coords. environment , & coords. platform , prefix) . await ?;
527+ let summary = install_lock (
528+ & lock,
529+ & coords. environment ,
530+ & coords. platform ,
531+ prefix,
532+ link_scripts,
533+ )
534+ . await ?;
496535 // Materialize the environment's activation hooks, recovered from the
497536 // manifest the lock was solved from: the embedded comment band if present,
498537 // else the registry's manifest sidecar. Best-effort: a release with no
@@ -1288,7 +1327,7 @@ mod tests {
12881327 std:: env:: temp_dir ( ) . join ( format ! ( "nepenthe-install-capstone-{}" , std:: process:: id( ) ) ) ;
12891328 let _ = std:: fs:: remove_dir_all ( & prefix) ;
12901329
1291- let summary = install_lock ( & lock, "app" , & platform, & prefix)
1330+ let summary = install_lock ( & lock, "app" , & platform, & prefix, LinkScripts :: Skip )
12921331 . await
12931332 . expect ( "install should succeed" ) ;
12941333 assert ! ( !summary. packages. is_empty( ) ) ;
@@ -1369,7 +1408,7 @@ mod tests {
13691408 let prefix =
13701409 std:: env:: temp_dir ( ) . join ( format ! ( "nepenthe-install-xplat-{}" , std:: process:: id( ) ) ) ;
13711410 let _ = std:: fs:: remove_dir_all ( & prefix) ;
1372- let summary = install_lock ( & lock, "app" , & host, & prefix)
1411+ let summary = install_lock ( & lock, "app" , & host, & prefix, LinkScripts :: Skip )
13731412 . await
13741413 . expect ( "install should succeed" ) ;
13751414 assert ! ( !summary. packages. is_empty( ) ) ;
0 commit comments