2
2
//!
3
3
//!
4
4
//!
5
- use std:: { env , fs, io} ;
5
+ use std:: { fs, io:: { self , ErrorKind } , path :: Path } ;
6
6
7
7
use sea_orm:: { ActiveModelTrait , DbConn , DbErr , Set , TransactionTrait } ;
8
8
@@ -11,7 +11,6 @@ use clap::Parser;
11
11
use crate :: internal:: db;
12
12
use crate :: internal:: model:: { config, reference} ;
13
13
use crate :: utils:: util:: { DATABASE , ROOT_DIR } ;
14
- use std:: path:: Path ;
15
14
use crate :: command:: branch;
16
15
17
16
#[ derive( Parser , Debug ) ]
@@ -23,6 +22,10 @@ pub struct InitArgs {
23
22
/// Set the initial branch name
24
23
#[ clap( short = 'b' , long, required = false ) ]
25
24
pub initial_branch : Option < String > ,
25
+
26
+ /// Create a repository in the specified directory
27
+ #[ clap( default_value = "." ) ]
28
+ pub repo_directory : String ,
26
29
}
27
30
28
31
/// Execute the init function
@@ -44,13 +47,41 @@ fn is_reinit(cur_dir: &Path) -> bool {
44
47
head_path. exists ( ) || bare_head_path. exists ( )
45
48
}
46
49
50
+ /// Check if the target directory is writable
51
+ fn is_writable ( cur_dir : & Path ) -> io:: Result < ( ) > {
52
+ match fs:: metadata ( cur_dir) {
53
+ Ok ( metadata) => {
54
+ // Check if the target directory is a directory
55
+ if !metadata. is_dir ( ) {
56
+ return Err ( io:: Error :: new (
57
+ io:: ErrorKind :: InvalidInput ,
58
+ "The target directory is not a directory." ,
59
+ ) ) ;
60
+ }
61
+ // Check permissions
62
+ if metadata. permissions ( ) . readonly ( ) {
63
+ return Err ( io:: Error :: new (
64
+ io:: ErrorKind :: PermissionDenied ,
65
+ "The target directory is read-only." ,
66
+ ) ) ;
67
+ }
68
+ }
69
+ Err ( e) if e. kind ( ) != ErrorKind :: NotFound => {
70
+ return Err ( e) ;
71
+ }
72
+ _ => { }
73
+ }
74
+ Ok ( ( ) )
75
+ }
76
+
47
77
/// Initialize a new Libra repository
48
78
/// This function creates the necessary directories and files for a new Libra repository.
49
79
/// It also sets up the database and the initial configuration.
50
80
#[ allow( dead_code) ]
51
81
pub async fn init ( args : InitArgs ) -> io:: Result < ( ) > {
52
82
// Get the current directory
53
- let cur_dir = env:: current_dir ( ) ?;
83
+ // let cur_dir = env::current_dir()?;
84
+ let cur_dir = Path :: new ( & args. repo_directory ) . to_path_buf ( ) ;
54
85
// Join the current directory with the root directory
55
86
let root_dir = if args. bare {
56
87
cur_dir. clone ( )
@@ -79,6 +110,14 @@ pub async fn init(args: InitArgs) -> io::Result<()> {
79
110
}
80
111
}
81
112
113
+ // Check if the target directory is writable
114
+ match is_writable ( & cur_dir) {
115
+ Ok ( _) => { }
116
+ Err ( e) => {
117
+ return Err ( e) ;
118
+ }
119
+ }
120
+
82
121
// Create .libra & sub-dirs
83
122
let dirs = [ "objects/pack" , "objects/info" , "info" ] ;
84
123
for dir in dirs {
@@ -188,6 +227,7 @@ mod tests {
188
227
use super :: * ;
189
228
use crate :: utils:: test;
190
229
use crate :: internal:: head:: Head ;
230
+ use std:: { env, os:: unix:: fs:: PermissionsExt } ;
191
231
192
232
pub fn verify_init ( base_dir : & Path ) {
193
233
@@ -217,7 +257,8 @@ mod tests {
217
257
async fn test_init ( ) {
218
258
// Set up the test environment without a Libra repository
219
259
test:: setup_clean_testing_env ( ) ;
220
- let args = InitArgs { bare : false , initial_branch : None } ;
260
+ let cur_dir = env:: current_dir ( ) . unwrap ( ) ;
261
+ let args = InitArgs { bare : false , initial_branch : None , repo_directory : cur_dir. to_str ( ) . unwrap ( ) . to_string ( ) } ;
221
262
// Run the init function
222
263
init ( args) . await . unwrap ( ) ;
223
264
@@ -229,33 +270,35 @@ mod tests {
229
270
verify_init ( libra_dir) ;
230
271
}
231
272
232
- //Test the init function with the --bare flag
273
+ /// Test the init function with the --bare flag
233
274
#[ tokio:: test]
234
275
async fn test_init_bare ( ) {
235
276
// Set up the test environment without a Libra repository
236
277
test:: setup_clean_testing_env ( ) ;
237
278
// Run the init function with --bare flag
238
- let args = InitArgs { bare : true , initial_branch : None } ;
279
+ let cur_dir = env:: current_dir ( ) . unwrap ( ) ;
280
+ let args = InitArgs { bare : true , initial_branch : None , repo_directory : cur_dir. to_str ( ) . unwrap ( ) . to_string ( ) } ;
239
281
// Run the init function
240
282
init ( args) . await . unwrap ( ) ;
241
283
242
284
let libra_dir = Path :: new ( "." ) ;
243
285
// Verify the contents of the other directory
244
286
verify_init ( libra_dir) ;
245
287
}
246
- //Test the init function with the --bare flag and an existing repository
288
+ /// Test the init function with the --bare flag and an existing repository
247
289
#[ tokio:: test]
248
290
async fn test_init_bare_with_existing_repo ( ) {
249
291
// Set up the test environment for a bare repository
250
292
test:: setup_clean_testing_env ( ) ;
251
293
252
294
// Initialize a bare repository
253
- let init_args = InitArgs { bare : false , initial_branch : None } ;
295
+ let cur_dir = env:: current_dir ( ) . unwrap ( ) ;
296
+ let init_args = InitArgs { bare : false , initial_branch : None , repo_directory : cur_dir. to_str ( ) . unwrap ( ) . to_string ( ) } ;
254
297
init ( init_args) . await . unwrap ( ) ; // Execute init for bare repository
255
298
256
299
// Simulate trying to reinitialize the bare repo
257
300
let result = async {
258
- let args = InitArgs { bare : true , initial_branch : None } ;
301
+ let args = InitArgs { bare : true , initial_branch : None , repo_directory : cur_dir . to_str ( ) . unwrap ( ) . to_string ( ) } ;
259
302
init ( args) . await
260
303
} ;
261
304
@@ -270,7 +313,8 @@ mod tests {
270
313
async fn test_init_with_initial_branch ( ) {
271
314
// Set up the test environment without a Libra repository
272
315
test:: setup_clean_testing_env ( ) ;
273
- let args = InitArgs { bare : false , initial_branch : Some ( "main" . to_string ( ) ) } ;
316
+ let cur_dir = env:: current_dir ( ) . unwrap ( ) ;
317
+ let args = InitArgs { bare : false , initial_branch : Some ( "main" . to_string ( ) ) , repo_directory : cur_dir. to_str ( ) . unwrap ( ) . to_string ( ) } ;
274
318
// Run the init function
275
319
init ( args) . await . unwrap ( ) ;
276
320
@@ -316,7 +360,8 @@ mod tests {
316
360
async fn test_invalid_branch_name ( branch_name : & str ) {
317
361
// Set up the test environment without a Libra repository
318
362
test:: setup_clean_testing_env ( ) ;
319
- let args = InitArgs { bare : false , initial_branch : Some ( branch_name. to_string ( ) ) } ;
363
+ let cur_dir = env:: current_dir ( ) . unwrap ( ) ;
364
+ let args = InitArgs { bare : false , initial_branch : Some ( branch_name. to_string ( ) ) , repo_directory : cur_dir. to_str ( ) . unwrap ( ) . to_string ( ) } ;
320
365
// Run the init function
321
366
let result = init ( args) . await ;
322
367
// Check for the error
@@ -325,4 +370,72 @@ mod tests {
325
370
assert ! ( err. to_string( ) . contains( "invalid branch name" ) ) ; // Check error message contains "invalid branch name"
326
371
}
327
372
373
+ /// Test the init function with [directory] parameter
374
+ #[ tokio:: test]
375
+ async fn test_init_with_directory ( ) {
376
+ // Set up the test environment without a Libra repository
377
+ test:: setup_clean_testing_env ( ) ;
378
+
379
+ // Create a test directory
380
+ let cur_dir = env:: current_dir ( ) . unwrap ( ) ;
381
+ let test_dir = cur_dir. join ( "test" ) ;
382
+
383
+ let args = InitArgs { bare : false , initial_branch : None , repo_directory : test_dir. to_str ( ) . unwrap ( ) . to_owned ( ) } ;
384
+ // Run the init function
385
+ init ( args) . await . unwrap ( ) ;
386
+
387
+ // Verify that the `.libra` directory exists
388
+ let libra_dir = test_dir. join ( ".libra" ) ;
389
+ assert ! ( libra_dir. exists( ) , ".libra directory does not exist" ) ;
390
+
391
+ // Verify the contents of the other directory
392
+ verify_init ( & libra_dir) ;
393
+ }
394
+
395
+ /// Test the init function with invalid [directory] parameter
396
+ #[ tokio:: test]
397
+ async fn test_init_with_invalid_directory ( ) {
398
+ // Set up the test environment without a Libra repository
399
+ test:: setup_clean_testing_env ( ) ;
400
+
401
+ // Create a test file instead of a directory
402
+ let cur_dir = env:: current_dir ( ) . unwrap ( ) ;
403
+ let test_dir = cur_dir. join ( "test.txt" ) ;
404
+
405
+ // Create a file with the same name as the test directory
406
+ fs:: File :: create ( & test_dir) . unwrap ( ) ;
407
+
408
+ let args = InitArgs { bare : false , initial_branch : None , repo_directory : test_dir. to_str ( ) . unwrap ( ) . to_owned ( ) } ;
409
+ // Run the init function
410
+ let result = init ( args) . await ;
411
+
412
+ // Check for the error
413
+ let err = result. unwrap_err ( ) ;
414
+ assert_eq ! ( err. kind( ) , std:: io:: ErrorKind :: InvalidInput ) ; // Check error type
415
+ assert ! ( err. to_string( ) . contains( "The target directory is not a directory" ) ) ; // Check error message
416
+ }
417
+
418
+ #[ tokio:: test]
419
+ async fn test_init_with_unauthorized_directory ( ) {
420
+ // Set up the test environment without a Libra repository
421
+ test:: setup_clean_testing_env ( ) ;
422
+
423
+ // Create a test directory
424
+ let cur_dir = env:: current_dir ( ) . unwrap ( ) ;
425
+ let test_dir = cur_dir. join ( "test" ) ;
426
+
427
+ // Create a directory with restricted permissions
428
+ fs:: create_dir ( & test_dir) . unwrap ( ) ;
429
+ fs:: set_permissions ( & test_dir, fs:: Permissions :: from_mode ( 0o444 ) ) . unwrap ( ) ;
430
+
431
+ let args = InitArgs { bare : false , initial_branch : None , repo_directory : test_dir. to_str ( ) . unwrap ( ) . to_owned ( ) } ;
432
+ // Run the init function
433
+ let result = init ( args) . await ;
434
+
435
+ // Check for the error
436
+ let err = result. unwrap_err ( ) ;
437
+ assert_eq ! ( err. kind( ) , std:: io:: ErrorKind :: PermissionDenied ) ; // Check error type
438
+ assert ! ( err. to_string( ) . contains( "The target directory is read-only" ) ) ; // Check error message
439
+ }
440
+
328
441
}
0 commit comments