Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions aria/contents/docs/libra/command/init/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ which sets up the repository without a working directory, containing only Libra
<Note type="danger" title="Git bare VS Libra bare">
**Note**:Unlike Git's `--bare`, the Libra init `--bare` initializes a bare repository that contains Libra's own SQLite database.
</Note>
- `--template <template-directory>`
Specify a directory from which templates will be used to initialize the repository.
- `-b`, `--initial-branch <INITIAL_BRANCH>`
Override the name of the initial branch
- `-h`, `--help` Print help
Expand Down
1 change: 1 addition & 0 deletions libra/src/command/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub async fn execute(args: CloneArgs) {
initial_branch: args.branch.clone(),
repo_directory: local_path.to_str().unwrap().to_string(),
quiet: false,
template: None,
};
command::init::execute(init_args).await;

Expand Down
114 changes: 82 additions & 32 deletions libra/src/command/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ pub struct InitArgs {
#[clap(long, required = false)]
pub bare: bool, // Default is false

/// directory from which templates will be used
#[clap(long = "template", name = "template-directory", required = false)]
pub template: Option<String>,

/// Set the initial branch name
#[clap(short = 'b', long, required = false)]
pub initial_branch: Option<String>,
Expand Down Expand Up @@ -82,6 +86,29 @@ fn is_writable(cur_dir: &Path) -> io::Result<()> {
Ok(())
}

/// Recursively copy the contents of the template directory to the destination directory.
///
/// # Behavior
/// - Directories are created as needed.
/// - Existing files in `dst` are NOT overwritten.
/// - Subdirectories are copied recursively.
fn copy_template(src: &Path, dst: &Path) -> io::Result<()> {
for entry in fs::read_dir(src)? {
let entry = entry?;
let file_type = entry.file_type()?;
let dest_path = dst.join(entry.file_name());

if file_type.is_dir() {
fs::create_dir_all(&dest_path)?;
copy_template(&entry.path(), &dest_path)?;
} else if !dest_path.exists() {
// Only copy if the file does not already exist
fs::copy(entry.path(), &dest_path)?;
Comment thread
slow2342 marked this conversation as resolved.
}
}
Ok(())
}
Comment thread
slow2342 marked this conversation as resolved.

/// Initialize a new Libra repository
/// This function creates the necessary directories and files for a new Libra repository.
/// It also sets up the database and the initial configuration.
Expand Down Expand Up @@ -127,41 +154,63 @@ pub async fn init(args: InitArgs) -> io::Result<()> {
}
}

// Create .libra & sub-dirs
let dirs = ["objects/pack", "objects/info", "info", "hooks"];
// ensure root dir exists
fs::create_dir_all(&root_dir)?;

// If a template path is provided, copy the template files to the root directory
if let Some(template_path) = &args.template {
let template_dir = Path::new(template_path);
if template_dir.exists() {
copy_template(template_dir, &root_dir)?;
} else {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("template directory '{}' does not exist", template_path),
));
}
} else {
// Create info & hooks
let dirs = ["info", "hooks"];
for dir in dirs {
fs::create_dir_all(root_dir.join(dir))?;
}
// Create info/exclude
// `include_str!` includes the file content while compiling
fs::write(
root_dir.join("info/exclude"),
include_str!("../../template/exclude"),
)?;
// Create .libra/description
fs::write(
root_dir.join("description"),
include_str!("../../template/description"),
)?;
// Create .libra/hooks/pre-commit.sh
fs::write(
root_dir.join("hooks").join("pre-commit.sh"),
include_str!("../../template/pre-commit.sh"),
)?;

// Set Permission
#[cfg(not(target_os = "windows"))]
{
use std::os::unix::fs::PermissionsExt;
let perms = fs::Permissions::from_mode(0o755);
fs::set_permissions(root_dir.join("hooks").join("pre-commit.sh"), perms)?;
}

// Create .libra/hooks/pre-commit.ps1
fs::write(
root_dir.join("hooks").join("pre-commit.ps1"),
include_str!("../../template/pre-commit.ps1"),
)?;
Comment thread
slow2342 marked this conversation as resolved.
}

// Complete .libra and sub-directories
let dirs = ["objects/pack", "objects/info"];
for dir in dirs {
fs::create_dir_all(root_dir.join(dir))?;
}
// Create info/exclude
// `include_str!` includes the file content while compiling
fs::write(
root_dir.join("info/exclude"),
include_str!("../../template/exclude"),
)?;
// Create .libra/description
fs::write(
root_dir.join("description"),
include_str!("../../template/description"),
)?;
// Create .libra/hooks/pre-commit.sh
fs::write(
root_dir.join("hooks").join("pre-commit.sh"),
include_str!("../../template/pre-commit.sh"),
)?;

// Set Permission
#[cfg(not(target_os = "windows"))]
{
use std::os::unix::fs::PermissionsExt;
let perms = fs::Permissions::from_mode(0o755);
fs::set_permissions(root_dir.join("hooks").join("pre-commit.sh"), perms)?;
}

// Create .libra/hooks/pre-commit.ps1
fs::write(
root_dir.join("hooks").join("pre-commit.ps1"),
include_str!("../../template/pre-commit.ps1"),
)?;

// Create database: .libra/libra.db
let conn;
Expand Down Expand Up @@ -206,6 +255,7 @@ pub async fn init(args: InitArgs) -> io::Result<()> {

Ok(())
}

/// Initialize the configuration for the Libra repository
/// This function creates the necessary configuration entries in the database.
async fn init_config(conn: &DbConn) -> Result<(), DbErr> {
Expand Down
1 change: 1 addition & 0 deletions libra/src/utils/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ pub async fn setup_with_new_libra_in(temp_path: impl AsRef<Path>) {
initial_branch: None,
repo_directory: temp_path.as_ref().to_str().unwrap().to_string(),
quiet: false,
template: None,
};
command::init::init(args).await.unwrap();
}
Expand Down
1 change: 1 addition & 0 deletions libra/tests/command/checkout_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ async fn test_checkout_module_functions() {
initial_branch: Some("main".to_string()),
repo_directory: temp_path.path().to_str().unwrap().to_string(),
quiet: false,
template: None,
};

init::init(init_args)
Expand Down
122 changes: 122 additions & 0 deletions libra/tests/command/init_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ async fn test_init() {
initial_branch: None,
repo_directory: target_dir.to_str().unwrap().to_string(),
quiet: false,
template: None,
};
// Run the init function
init(args).await.unwrap();
Expand All @@ -44,6 +45,117 @@ async fn test_init() {
verify_init(libra_dir.as_path());
}

#[tokio::test]
#[serial]
/// Test the init function with a template directory
async fn test_init_template() {
use std::fs;
use tempfile::tempdir;

// Create a temporary target directory for the new repo
let target_dir = tempdir().unwrap().keep();

// Create a temporary template directory
let template_dir = tempdir().unwrap();

// Set up template structure similar to Git template
fs::create_dir_all(template_dir.path().join("objects/pack")).unwrap();
fs::create_dir_all(template_dir.path().join("objects/info")).unwrap();
fs::create_dir_all(template_dir.path().join("info")).unwrap();

// Add description file in the template
fs::write(
template_dir.path().join("description"),
"Template repository",
)
.unwrap();

// Add info/exclude file in the template
fs::write(template_dir.path().join("info/exclude"), "").unwrap();

// Prepare init arguments with template path
let args = InitArgs {
bare: false,
initial_branch: None,
repo_directory: target_dir.to_str().unwrap().to_string(),
quiet: false,
template: Some(template_dir.path().to_str().unwrap().to_string()),
};

// Run the init function
init(args).await.unwrap();

// Verify that the `.libra` directory exists
let libra_dir = target_dir.join(".libra");
assert!(libra_dir.exists(), ".libra directory does not exist");

// Verify the repository initialization structure
verify_init(libra_dir.as_path());

// --- Additional checks for template contents ---

// Verify that description file is copied from template
let description_path = libra_dir.join("description");
assert!(
description_path.exists(),
"Template description file not copied"
);

// Verify that info/exclude file is copied from template
let exclude_path = libra_dir.join("info/exclude");
assert!(
exclude_path.exists(),
"Template info/exclude file not copied"
);

// Verify that objects subdirectories are copied from template
assert!(
libra_dir.join("objects/pack").exists(),
"Template objects/pack directory not copied"
);
assert!(
libra_dir.join("objects/info").exists(),
"Template objects/info directory not copied"
);
}

#[tokio::test]
#[serial]
/// Test the init function with an invalid template path
async fn test_init_with_invalid_template_path() {
use tempfile::tempdir;

// Create a temporary target directory for the new repo
let target_dir = tempdir().unwrap().keep();

// Provide a non-existent template path
let invalid_template_path = "/path/to/nonexistent/template";
Comment thread
slow2342 marked this conversation as resolved.

let args = InitArgs {
bare: false,
initial_branch: None,
repo_directory: target_dir.to_str().unwrap().to_string(),
quiet: false,
template: Some(invalid_template_path.to_string()),
};

// Run the init function and expect it to return an error
let result = init(args).await;

// Verify that the function returns an error due to invalid template path
assert!(
result.is_err(),
"Init should fail when template path does not exist"
);

// Optionally, verify the error kind/message if your init function provides it
if let Err(err) = result {
// Uncomment and adjust depending on your error type
// assert_eq!(err.kind(), Some(ExpectedErrorKind::NotFound));
println!("Received expected error: {:?}", err);
}
}

#[tokio::test]
#[serial]
/// Test the init function with the --bare flag
Expand All @@ -57,13 +169,15 @@ async fn test_init_bare() {
initial_branch: None,
repo_directory: target_dir.to_str().unwrap().to_string(),
quiet: false,
template: None,
};
// Run the init function
init(args).await.unwrap();

// Verify the contents of the other directory
verify_init(target_dir.as_path());
}

Comment thread
slow2342 marked this conversation as resolved.
#[tokio::test]
#[serial]
/// Test the init function with the --bare flag and an existing repository
Expand All @@ -76,6 +190,7 @@ async fn test_init_bare_with_existing_repo() {
initial_branch: None,
repo_directory: target_dir.to_str().unwrap().to_string(),
quiet: false,
template: None,
};
init(init_args).await.unwrap(); // Execute init for bare repository

Expand All @@ -86,6 +201,7 @@ async fn test_init_bare_with_existing_repo() {
initial_branch: None,
repo_directory: target_dir.to_str().unwrap().to_string(),
quiet: false,
template: None,
};
init(args).await
};
Expand All @@ -110,6 +226,7 @@ async fn test_init_with_initial_branch() {
initial_branch: Some("main".to_string()),
repo_directory: temp_path.path().to_str().unwrap().to_string(),
quiet: false,
template: None,
};
// Run the init function
init(args).await.unwrap();
Expand Down Expand Up @@ -157,6 +274,7 @@ async fn test_invalid_branch_name(branch_name: &str) {
initial_branch: Some(branch_name.to_string()),
repo_directory: target_dir.to_str().unwrap().to_string(),
quiet: false,
template: None,
};
// Run the init function
let result = init(args).await;
Expand All @@ -180,6 +298,7 @@ async fn test_init_with_directory() {
initial_branch: None,
repo_directory: test_dir.to_str().unwrap().to_owned(),
quiet: false,
template: None,
};
// Run the init function
init(args).await.unwrap();
Expand Down Expand Up @@ -209,6 +328,7 @@ async fn test_init_with_invalid_directory() {
initial_branch: None,
repo_directory: test_dir.to_str().unwrap().to_owned(),
quiet: false,
template: None,
};
// Run the init function
let result = init(args).await;
Expand Down Expand Up @@ -249,6 +369,7 @@ async fn test_init_with_unauthorized_directory() {
initial_branch: None,
repo_directory: test_dir.to_str().unwrap().to_owned(),
quiet: false,
template: None,
};
// Run the init function
let result = init(args).await;
Expand All @@ -272,6 +393,7 @@ async fn test_init_quiet() {
initial_branch: None,
repo_directory: target_dir.to_str().unwrap().to_string(),
quiet: true,
template: None,
};
// Run the init function
init(args).await.unwrap();
Expand Down
Loading