Skip to content

Commit 839b810

Browse files
authored
Merge pull request #819 from Ricky-Daxia/feature/init-with-directory
[libra] 为 libra init 命令实现 [directory] 参数
2 parents 92a79ca + 9f02963 commit 839b810

File tree

4 files changed

+129
-14
lines changed

4 files changed

+129
-14
lines changed

aria/contents/docs/libra/command/init/index.mdx

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@ which sets up the repository without a working directory, containing only Libra
2727
that contains Libra's own SQLite database.
2828
- `-b`, `--initial-branch <INITIAL_BRANCH>`
2929
Override the name of the initial branch
30-
- `-h`, `--help` Print help
30+
- `-h`, `--help` Print help
31+
- `[directory]`
32+
Initialize the repository with specified directory

libra/src/command/clone.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub async fn execute(args: CloneArgs) {
7171

7272
// CAUTION: change [current_dir] to the repo directory
7373
env::set_current_dir(&local_path).unwrap();
74-
let init_args = command::init::InitArgs { bare: false, initial_branch: None };
74+
let init_args = command::init::InitArgs { bare: false, initial_branch: None, repo_directory: local_path.to_str().unwrap().to_string() };
7575
command::init::execute(init_args).await;
7676

7777
/* fetch remote */

libra/src/command/init.rs

+124-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
//!
44
//!
5-
use std::{env, fs, io};
5+
use std::{fs, io::{self, ErrorKind}, path::Path};
66

77
use sea_orm::{ActiveModelTrait, DbConn, DbErr, Set, TransactionTrait};
88

@@ -11,7 +11,6 @@ use clap::Parser;
1111
use crate::internal::db;
1212
use crate::internal::model::{config, reference};
1313
use crate::utils::util::{DATABASE, ROOT_DIR};
14-
use std::path::Path;
1514
use crate::command::branch;
1615

1716
#[derive(Parser, Debug)]
@@ -23,6 +22,10 @@ pub struct InitArgs {
2322
/// Set the initial branch name
2423
#[clap(short = 'b', long, required = false)]
2524
pub initial_branch: Option<String>,
25+
26+
/// Create a repository in the specified directory
27+
#[clap(default_value = ".")]
28+
pub repo_directory: String,
2629
}
2730

2831
/// Execute the init function
@@ -44,13 +47,41 @@ fn is_reinit(cur_dir: &Path) -> bool {
4447
head_path.exists() || bare_head_path.exists()
4548
}
4649

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+
4777
/// Initialize a new Libra repository
4878
/// This function creates the necessary directories and files for a new Libra repository.
4979
/// It also sets up the database and the initial configuration.
5080
#[allow(dead_code)]
5181
pub async fn init(args: InitArgs) -> io::Result<()> {
5282
// 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();
5485
// Join the current directory with the root directory
5586
let root_dir = if args.bare{
5687
cur_dir.clone()
@@ -79,6 +110,14 @@ pub async fn init(args: InitArgs) -> io::Result<()> {
79110
}
80111
}
81112

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+
82121
// Create .libra & sub-dirs
83122
let dirs = ["objects/pack", "objects/info", "info"];
84123
for dir in dirs {
@@ -188,6 +227,7 @@ mod tests {
188227
use super::*;
189228
use crate::utils::test;
190229
use crate::internal::head::Head;
230+
use std::{env, os::unix::fs::PermissionsExt};
191231

192232
pub fn verify_init(base_dir: &Path){
193233

@@ -217,7 +257,8 @@ mod tests {
217257
async fn test_init() {
218258
// Set up the test environment without a Libra repository
219259
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() };
221262
// Run the init function
222263
init(args).await.unwrap();
223264

@@ -229,33 +270,35 @@ mod tests {
229270
verify_init(libra_dir);
230271
}
231272

232-
//Test the init function with the --bare flag
273+
/// Test the init function with the --bare flag
233274
#[tokio::test]
234275
async fn test_init_bare() {
235276
// Set up the test environment without a Libra repository
236277
test::setup_clean_testing_env();
237278
// 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() };
239281
// Run the init function
240282
init(args).await.unwrap();
241283

242284
let libra_dir = Path::new(".");
243285
// Verify the contents of the other directory
244286
verify_init(libra_dir);
245287
}
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
247289
#[tokio::test]
248290
async fn test_init_bare_with_existing_repo() {
249291
// Set up the test environment for a bare repository
250292
test::setup_clean_testing_env();
251293

252294
// 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() };
254297
init(init_args).await.unwrap(); // Execute init for bare repository
255298

256299
// Simulate trying to reinitialize the bare repo
257300
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() };
259302
init(args).await
260303
};
261304

@@ -270,7 +313,8 @@ mod tests {
270313
async fn test_init_with_initial_branch() {
271314
// Set up the test environment without a Libra repository
272315
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() };
274318
// Run the init function
275319
init(args).await.unwrap();
276320

@@ -316,7 +360,8 @@ mod tests {
316360
async fn test_invalid_branch_name(branch_name: &str) {
317361
// Set up the test environment without a Libra repository
318362
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() };
320365
// Run the init function
321366
let result = init(args).await;
322367
// Check for the error
@@ -325,4 +370,72 @@ mod tests {
325370
assert!(err.to_string().contains("invalid branch name")); // Check error message contains "invalid branch name"
326371
}
327372

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+
328441
}

libra/src/utils/test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ pub fn setup_clean_testing_env() {
106106
/// switch to test dir and create a new .libra
107107
pub async fn setup_with_new_libra() {
108108
setup_clean_testing_env();
109-
let args = command::init::InitArgs { bare: false, initial_branch: None };
109+
let args = command::init::InitArgs { bare: false, initial_branch: None, repo_directory: util::cur_dir().to_str().unwrap().to_string() };
110110
command::init::init(args).await.unwrap();
111111
}
112112

0 commit comments

Comments
 (0)