@@ -2,6 +2,7 @@ use super::*;
2
2
use crate :: core:: Target ;
3
3
use crate :: tasks:: * ;
4
4
use clap:: Arg ;
5
+ use std:: path:: PathBuf ;
5
6
use tracing_batteries:: prelude:: * ;
6
7
7
8
pub struct CloneCommand ;
@@ -31,12 +32,46 @@ impl CommandRunnable for CloneCommand {
31
32
"You didn't specify the repository you wanted to clone." ,
32
33
"Remember to specify a repository name like this: 'git-tool clone gh:sierrasoftworks/git-tool'." ) ) ?;
33
34
34
- let repo = core. resolver ( ) . get_best_repo ( repo_name) ?;
35
-
36
- if !repo. exists ( ) {
37
- match sequence ! [ GitClone { } ] . apply_repo ( core, & repo) . await {
38
- Ok ( ( ) ) => { }
39
- Err ( e) => return Err ( e) ,
35
+ if let Some ( file_path) = repo_name. strip_prefix ( '@' ) {
36
+ // Load the list of repos to clone from a file
37
+ let file_path: PathBuf = file_path. parse ( ) . map_err ( |e| {
38
+ errors:: user_with_internal (
39
+ "The specified file path is not valid." ,
40
+ "Please make sure you are specifying a valid file path for your import file." ,
41
+ e,
42
+ )
43
+ } ) ?;
44
+
45
+ let file = std:: fs:: read_to_string ( & file_path) . map_err ( |e| {
46
+ errors:: user_with_internal (
47
+ "Could not read the specified clone file." ,
48
+ "Please make sure the file exists and is readable." ,
49
+ e,
50
+ )
51
+ } ) ?;
52
+
53
+ let operation = sequence ! [ GitClone { } ] ;
54
+
55
+ for line in file. lines ( ) {
56
+ if line. trim_start ( ) . is_empty ( ) || line. trim_start ( ) . starts_with ( '#' ) {
57
+ continue ;
58
+ }
59
+
60
+ let repo = core. resolver ( ) . get_best_repo ( line. trim ( ) ) ?;
61
+ writeln ! ( core. output( ) , "{}" , repo) ?;
62
+ match operation. apply_repo ( core, & repo) . await {
63
+ Ok ( ( ) ) => { }
64
+ Err ( e) => return Err ( e) ,
65
+ }
66
+ }
67
+ } else {
68
+ let repo = core. resolver ( ) . get_best_repo ( repo_name) ?;
69
+
70
+ if !repo. exists ( ) {
71
+ match sequence ! [ GitClone { } ] . apply_repo ( core, & repo) . await {
72
+ Ok ( ( ) ) => { }
73
+ Err ( e) => return Err ( e) ,
74
+ }
40
75
}
41
76
}
42
77
@@ -127,4 +162,61 @@ features:
127
162
Err ( err) => panic ! ( "{}" , err. message( ) ) ,
128
163
}
129
164
}
165
+
166
+ #[ tokio:: test]
167
+ #[ cfg_attr( feature = "pure-tests" , ignore) ]
168
+ async fn run_batch ( ) {
169
+ let cmd = CloneCommand { } ;
170
+
171
+ let temp = tempdir ( ) . unwrap ( ) ;
172
+
173
+ let args = cmd. app ( ) . get_matches_from ( vec ! [
174
+ "clone" ,
175
+ format!( "@{}" , temp. path( ) . join( "import.txt" ) . display( ) ) . as_str( ) ,
176
+ ] ) ;
177
+
178
+ let cfg = Config :: from_str (
179
+ "
180
+ directory: /dev
181
+
182
+ apps:
183
+ - name: test-app
184
+ command: test
185
+ args:
186
+ - '{{ .Target.Name }}'
187
+
188
+ features:
189
+ http_transport: true
190
+ " ,
191
+ )
192
+ . unwrap ( ) ;
193
+
194
+ let temp_path = temp. path ( ) . to_path_buf ( ) ;
195
+
196
+ std:: fs:: write ( temp. path ( ) . join ( "import.txt" ) , "gh:git-fixtures/basic" )
197
+ . expect ( "writing should succeed" ) ;
198
+
199
+ let core = Core :: builder ( )
200
+ . with_config ( cfg)
201
+ . with_mock_launcher ( |mock| {
202
+ mock. expect_run ( ) . never ( ) ;
203
+ } )
204
+ . with_mock_resolver ( |mock| {
205
+ let temp_path = temp_path. clone ( ) ;
206
+ mock. expect_get_best_repo ( )
207
+ . once ( )
208
+ . with ( mockall:: predicate:: eq ( "gh:git-fixtures/basic" ) )
209
+ . returning ( move |_| {
210
+ Ok ( Repo :: new ( "gh:git-fixtures/basic" , temp_path. join ( "repo" ) ) )
211
+ } ) ;
212
+ } )
213
+ . build ( ) ;
214
+
215
+ match cmd. run ( & core, & args) . await {
216
+ Ok ( status) => {
217
+ assert_eq ! ( status, 0 ) ;
218
+ }
219
+ Err ( err) => panic ! ( "{}" , err. message( ) ) ,
220
+ }
221
+ }
130
222
}
0 commit comments