@@ -15,8 +15,8 @@ use futures::{
15
15
StreamExt , TryStreamExt ,
16
16
} ;
17
17
use indicatif:: ProgressStyle ;
18
- use rattler_conda_types:: Platform ;
19
- use rattler_lock:: { LockFile , Package } ;
18
+ use rattler_conda_types:: { PackageRecord , Platform } ;
19
+ use rattler_lock:: { CondaPackage , LockFile , Package } ;
20
20
use rattler_networking:: { AuthenticationMiddleware , AuthenticationStorage } ;
21
21
use reqwest_middleware:: ClientWithMiddleware ;
22
22
use tokio_tar:: Builder ;
@@ -65,9 +65,20 @@ pub async fn pack(options: PackOptions) -> Result<()> {
65
65
options. platform. as_str( )
66
66
) ) ?;
67
67
68
+ let mut conda_packages = Vec :: new ( ) ;
69
+
70
+ for package in packages {
71
+ match package {
72
+ Package :: Conda ( p) => conda_packages. push ( p) ,
73
+ Package :: Pypi ( _) => {
74
+ anyhow:: bail!( "pypi packages are not supported in pixi-pack" ) ;
75
+ }
76
+ }
77
+ }
78
+
68
79
// Download packages to temporary directory.
69
- tracing:: info!( "Downloading {} packages" , packages . len( ) ) ;
70
- let bar = indicatif:: ProgressBar :: new ( packages . len ( ) as u64 ) ;
80
+ tracing:: info!( "Downloading {} packages" , conda_packages . len( ) ) ;
81
+ let bar = indicatif:: ProgressBar :: new ( conda_packages . len ( ) as u64 ) ;
71
82
bar. set_style (
72
83
ProgressStyle :: with_template (
73
84
"[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}" ,
@@ -81,7 +92,7 @@ pub async fn pack(options: PackOptions) -> Result<()> {
81
92
82
93
let channel_dir = output_folder. path ( ) . join ( CHANNEL_DIRECTORY_NAME ) ;
83
94
84
- stream:: iter ( packages )
95
+ stream:: iter ( conda_packages . iter ( ) )
85
96
. map ( Ok )
86
97
. try_for_each_concurrent ( 50 , |package| async {
87
98
download_package ( & client, package, & channel_dir) . await ?;
@@ -105,6 +116,13 @@ pub async fn pack(options: PackOptions) -> Result<()> {
105
116
let metadata = serde_json:: to_string_pretty ( & options. metadata ) ?;
106
117
metadata_file. write_all ( metadata. as_bytes ( ) ) . await ?;
107
118
119
+ // Create environment file.
120
+ create_environment_file (
121
+ output_folder. path ( ) ,
122
+ conda_packages. iter ( ) . map ( |p| p. package_record ( ) ) ,
123
+ )
124
+ . await ?;
125
+
108
126
// Pack = archive + compress the contents.
109
127
archive_directory ( output_folder. path ( ) , & options. output_file , options. level )
110
128
. await
@@ -146,25 +164,21 @@ fn reqwest_client_from_auth_storage(auth_file: Option<PathBuf>) -> Result<Client
146
164
/// Download a conda package to a given output directory.
147
165
async fn download_package (
148
166
client : & ClientWithMiddleware ,
149
- package : Package ,
167
+ package : & CondaPackage ,
150
168
output_dir : & Path ,
151
169
) -> Result < ( ) > {
152
- let conda_package = package
153
- . as_conda ( )
154
- . ok_or ( anyhow ! ( "package is not a conda package" ) ) ?; // TODO: we might want to skip here
155
-
156
- let output_dir = output_dir. join ( & conda_package. package_record ( ) . subdir ) ;
170
+ let output_dir = output_dir. join ( & package. package_record ( ) . subdir ) ;
157
171
create_dir_all ( & output_dir)
158
172
. await
159
173
. map_err ( |e| anyhow ! ( "could not create download directory: {}" , e) ) ?;
160
174
161
- let file_name = conda_package
175
+ let file_name = package
162
176
. file_name ( )
163
177
. ok_or ( anyhow ! ( "could not get file name" ) ) ?;
164
178
let mut dest = File :: create ( output_dir. join ( file_name) ) . await ?;
165
179
166
- tracing:: debug!( "Fetching package {}" , conda_package . url( ) ) ;
167
- let mut response = client. get ( conda_package . url ( ) . to_string ( ) ) . send ( ) . await ?;
180
+ tracing:: debug!( "Fetching package {}" , package . url( ) ) ;
181
+ let mut response = client. get ( package . url ( ) . to_string ( ) ) . send ( ) . await ?;
168
182
169
183
while let Some ( chunk) = response. chunk ( ) . await ? {
170
184
dest. write_all ( & chunk) . await ?;
@@ -211,3 +225,34 @@ async fn archive_directory(
211
225
212
226
Ok ( ( ) )
213
227
}
228
+
229
+ async fn create_environment_file (
230
+ destination : & Path ,
231
+ packages : impl IntoIterator < Item = & PackageRecord > ,
232
+ ) -> Result < ( ) > {
233
+ let environment_path = destination. join ( "environment.yml" ) ;
234
+
235
+ let mut environment = String :: new ( ) ;
236
+
237
+ environment. push_str ( "channels:\n " ) ;
238
+ environment. push_str ( & format ! ( " - ./{CHANNEL_DIRECTORY_NAME}\n " , ) ) ;
239
+ environment. push_str ( " - nodefaults\n " ) ;
240
+ environment. push_str ( "dependencies:\n " ) ;
241
+
242
+ for package in packages {
243
+ let match_spec_str = format ! (
244
+ "{}={}={}" ,
245
+ package. name. as_normalized( ) ,
246
+ package. version,
247
+ package. build,
248
+ ) ;
249
+
250
+ environment. push_str ( & format ! ( " - {}\n " , match_spec_str) ) ;
251
+ }
252
+
253
+ fs:: write ( environment_path, environment)
254
+ . await
255
+ . map_err ( |e| anyhow ! ( "Could not write environment file: {}" , e) ) ?;
256
+
257
+ Ok ( ( ) )
258
+ }
0 commit comments