@@ -4,7 +4,8 @@ use crate::shell::SupportedShell;
44
55use log:: { debug, error, info, warn} ;
66use serde:: Deserialize ;
7- use std:: io:: { Error , ErrorKind } ;
7+ use std:: fs;
8+ use std:: io;
89use std:: path:: { Component , Path , PathBuf } ;
910use std:: process:: ExitCode ;
1011
@@ -21,7 +22,7 @@ pub enum Subcommand {
2122 /// Create an archive from an environment. Requires `conda-pack` to be in the environment.
2223 Pack ( PackArgs ) ,
2324 /// Unpack an archive to create an environment from it.
24- Unpack ( CommonEnvArgs ) ,
25+ Unpack ( UnpackArgs ) ,
2526 /// List existing environments
2627 List ,
2728 /// Display information about the micromamba setup
@@ -73,13 +74,23 @@ pub struct PackArgs {
7374 /// Common environment arguments
7475 #[ command( flatten) ]
7576 common : CommonEnvArgs ,
76- /// Output path/filename of the packed environment. If not specified, the
77- /// same method is used to determine the environment name as for the
78- /// "--name" parameter, and the default name is <env_name>.tar.gz
77+ /// Output path/filename of the packed environment, ending in .tar.gz. If
78+ /// not specified, the same method is used to determine the environment name
79+ /// as for the "--name" parameter, and the default name is <env_name>.tar.gz
7980 #[ arg( long, short, value_name = "OUTPUT" ) ]
8081 output : Option < String > ,
8182}
8283
84+ #[ derive( Debug , clap:: Args ) ]
85+ pub struct UnpackArgs {
86+ /// Common environment arguments
87+ #[ command( flatten) ]
88+ common : CommonEnvArgs ,
89+ /// Path to a packed environment archive (ending in .tar.gz)
90+ #[ arg( value_name = "ARCHIVE" ) ]
91+ archive_path : PathBuf ,
92+ }
93+
8394/// Contains the fields we need from a parsed environment file.
8495#[ derive( Deserialize ) ]
8596struct RobotmkEnv {
@@ -102,9 +113,10 @@ struct PostBuildCommand {
102113
103114impl RobotmkSetup {
104115 /// Attempt to parse a setup file.
105- fn from_path < P : AsRef < Path > > ( path : P ) -> Result < Self , std :: io:: Error > {
116+ fn from_path < P : AsRef < Path > > ( path : P ) -> Result < Self , io:: Error > {
106117 let contents = std:: fs:: read_to_string ( path) ?;
107- serde_yaml_ng:: from_str ( & contents) . map_err ( |e| Error :: new ( ErrorKind :: InvalidData , e) )
118+ serde_yaml_ng:: from_str ( & contents)
119+ . map_err ( |e| io:: Error :: new ( io:: ErrorKind :: InvalidData , e) )
108120 }
109121
110122 fn run_post_create ( self , config : & Config , env_name : & str ) -> Result < ( ) , ExitCode > {
@@ -129,9 +141,9 @@ impl RobotmkSetup {
129141}
130142
131143/// Attempt to parse an environment file.
132- fn parse_env_yaml < P : AsRef < Path > > ( path : P ) -> Result < RobotmkEnv , std :: io:: Error > {
144+ fn parse_env_yaml < P : AsRef < Path > > ( path : P ) -> Result < RobotmkEnv , io:: Error > {
133145 let contents = std:: fs:: read_to_string ( path) ?;
134- serde_yaml_ng:: from_str ( & contents) . map_err ( |e| Error :: new ( ErrorKind :: InvalidData , e) )
146+ serde_yaml_ng:: from_str ( & contents) . map_err ( |e| io :: Error :: new ( io :: ErrorKind :: InvalidData , e) )
135147}
136148
137149pub fn determine_env_name ( explicit_name : Option < String > , env_yaml_path : & Path ) -> Option < String > {
@@ -233,7 +245,7 @@ pub fn run(config: Config, subcommand: Subcommand) -> Result<(), ExitCode> {
233245 Some ( path) => ( path, true ) ,
234246 } ;
235247 let setup = match RobotmkSetup :: from_path ( & filename) {
236- Err ( e) if e. kind ( ) == ErrorKind :: NotFound => {
248+ Err ( e) if e. kind ( ) == io :: ErrorKind :: NotFound => {
237249 if required {
238250 // Handled above, but theoretical race here, so handle it
239251 error ! ( "The specified setup file was not found" ) ;
@@ -365,10 +377,73 @@ pub fn run(config: Config, subcommand: Subcommand) -> Result<(), ExitCode> {
365377 )
366378 . into ( )
367379 }
368- _ => {
369- println ! ( "{:?}" , config) ;
370- println ! ( "{:?}" , subcommand) ;
371- Ok ( ( ) )
380+ Subcommand :: Unpack ( args) => {
381+ fn archive_name_to_env_name ( archive_path : & Path ) -> Option < & str > {
382+ let filename = archive_path. file_name ( ) ?. to_str ( ) ?;
383+ filename
384+ . strip_suffix ( ".tar.gz" )
385+ . or_else ( || filename. strip_suffix ( ".tgz" ) )
386+ }
387+ let env_name = match args. common . name {
388+ Some ( name) => name,
389+ None => match archive_name_to_env_name ( & args. archive_path ) {
390+ Some ( name) => {
391+ info ! (
392+ "Using '{}' as environment name, based on archive filename" ,
393+ name
394+ ) ;
395+ String :: from ( name)
396+ }
397+ None => {
398+ error ! (
399+ "Could not determine environment name from archive filename. Please specify an environment name with --name."
400+ ) ;
401+ return Err ( ExitCode :: FAILURE ) ;
402+ }
403+ } ,
404+ } ;
405+
406+ // TODO: We really need a more generic error type to avoid this kind of mapping everywhere
407+ let target_env_path = micromamba:: create_env_dir ( & config, & env_name) . map_err ( |e| {
408+ error ! ( "{}" , e) ;
409+ ExitCode :: FAILURE
410+ } ) ?;
411+
412+ // Send the archive to flate2 to decompress and untar
413+ info ! (
414+ "Unpacking archive '{}' to create environment '{}'" ,
415+ args. archive_path. display( ) ,
416+ env_name
417+ ) ;
418+ debug ! ( "Opening '{}' for read" , args. archive_path. display( ) ) ;
419+ let archive_file = fs:: File :: open ( & args. archive_path ) . map_err ( |e| {
420+ error ! ( "{}" , e) ;
421+ ExitCode :: FAILURE
422+ } ) ?;
423+ let decompressor = flate2:: read:: GzDecoder :: new ( archive_file) ;
424+ let mut archive = tar:: Archive :: new ( decompressor) ;
425+ archive. unpack ( & target_env_path) . map_err ( |e| {
426+ error ! (
427+ "Could not unpack archive to '{}': {}" ,
428+ target_env_path. display( ) ,
429+ e
430+ ) ;
431+ ExitCode :: FAILURE
432+ } ) ?;
433+ info ! (
434+ "Successfully unpacked environment to '{}'" ,
435+ target_env_path. display( )
436+ ) ;
437+
438+ info ! ( "Running 'conda-unpack' in the new environment to fix paths..." ) ;
439+ let result = micromamba (
440+ & config,
441+ vec ! [ "run" , "--name" , & env_name, "conda-unpack" ] ,
442+ config. verbose ,
443+ ) ;
444+ let rc = result. exit_code ( ) ;
445+ dump_micromamba_captured_output_on_error ( & result, rc) ;
446+ result. into ( )
372447 }
373448 }
374449}
0 commit comments