...
This commit is contained in:
115
src/rhai/git.rs
Normal file
115
src/rhai/git.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
//! Rhai wrappers for Git module functions
|
||||
//!
|
||||
//! This module provides Rhai wrappers for the functions in the Git module.
|
||||
|
||||
use rhai::{Engine, EvalAltResult, Array, Dynamic};
|
||||
use crate::git::{self, GitError};
|
||||
|
||||
/// Register Git module functions with the Rhai engine
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `engine` - The Rhai engine to register the functions with
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
|
||||
pub fn register_git_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
||||
// Register basic git functions
|
||||
engine.register_fn("git_clone", git_clone);
|
||||
engine.register_fn("git_list", git_list);
|
||||
engine.register_fn("git_update", git_update);
|
||||
engine.register_fn("git_update_force", git_update_force);
|
||||
engine.register_fn("git_update_commit", git_update_commit);
|
||||
engine.register_fn("git_update_commit_push", git_update_commit_push);
|
||||
engine.register_fn("git_has_changes", has_git_changes);
|
||||
engine.register_fn("git_find_repos", find_matching_repos);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Helper functions for error conversion
|
||||
fn git_error_to_rhai_error<T>(result: Result<T, GitError>) -> Result<T, Box<EvalAltResult>> {
|
||||
result.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Git error: {}", e).into(),
|
||||
rhai::Position::NONE
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
// Git Function Wrappers
|
||||
//
|
||||
|
||||
/// Wrapper for git::git_clone
|
||||
///
|
||||
/// Clones a git repository to a standardized location in the user's home directory.
|
||||
pub fn git_clone(url: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
git_error_to_rhai_error(git::git_clone(url))
|
||||
}
|
||||
|
||||
/// Wrapper for git::git_list
|
||||
///
|
||||
/// Lists all git repositories found in the user's ~/code directory.
|
||||
pub fn git_list() -> Result<Array, Box<EvalAltResult>> {
|
||||
let repos = git_error_to_rhai_error(git::git_list())?;
|
||||
|
||||
// Convert Vec<String> to Rhai Array
|
||||
let mut array = Array::new();
|
||||
for repo in repos {
|
||||
array.push(Dynamic::from(repo));
|
||||
}
|
||||
|
||||
Ok(array)
|
||||
}
|
||||
|
||||
/// Wrapper for git::has_git_changes
|
||||
///
|
||||
/// Checks if a git repository has uncommitted changes.
|
||||
pub fn has_git_changes(repo_path: &str) -> Result<bool, Box<EvalAltResult>> {
|
||||
git_error_to_rhai_error(git::has_git_changes(repo_path))
|
||||
}
|
||||
|
||||
/// Wrapper for git::find_matching_repos
|
||||
///
|
||||
/// Finds repositories matching a pattern or partial path.
|
||||
pub fn find_matching_repos(pattern: &str) -> Result<Array, Box<EvalAltResult>> {
|
||||
let repos = git_error_to_rhai_error(git::find_matching_repos(pattern))?;
|
||||
|
||||
// Convert Vec<String> to Rhai Array
|
||||
let mut array = Array::new();
|
||||
for repo in repos {
|
||||
array.push(Dynamic::from(repo));
|
||||
}
|
||||
|
||||
Ok(array)
|
||||
}
|
||||
|
||||
/// Wrapper for git::git_update
|
||||
///
|
||||
/// Updates a git repository by pulling the latest changes.
|
||||
pub fn git_update(repo_path: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
git_error_to_rhai_error(git::git_update(repo_path))
|
||||
}
|
||||
|
||||
/// Wrapper for git::git_update_force
|
||||
///
|
||||
/// Force updates a git repository by discarding local changes and pulling the latest changes.
|
||||
pub fn git_update_force(repo_path: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
git_error_to_rhai_error(git::git_update_force(repo_path))
|
||||
}
|
||||
|
||||
/// Wrapper for git::git_update_commit
|
||||
///
|
||||
/// Commits changes in a git repository and then updates it by pulling the latest changes.
|
||||
pub fn git_update_commit(repo_path: &str, message: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
git_error_to_rhai_error(git::git_update_commit(repo_path, message))
|
||||
}
|
||||
|
||||
/// Wrapper for git::git_update_commit_push
|
||||
///
|
||||
/// Commits changes in a git repository and pushes them to the remote.
|
||||
pub fn git_update_commit_push(repo_path: &str, message: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
git_error_to_rhai_error(git::git_update_commit_push(repo_path, message))
|
||||
}
|
@@ -7,6 +7,8 @@ mod error;
|
||||
mod os;
|
||||
mod process;
|
||||
mod buildah;
|
||||
mod nerdctl;
|
||||
mod git;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -35,7 +37,28 @@ pub use process::{
|
||||
which, kill, process_list, process_get
|
||||
};
|
||||
|
||||
pub use buildah::*;
|
||||
// Re-export buildah functions
|
||||
pub use buildah::register_bah_module;
|
||||
pub use buildah::{
|
||||
bah_from, bah_run, bah_run_with_isolation, bah_copy, bah_add, bah_commit,
|
||||
bah_remove, bah_list, bah_build_with_options,
|
||||
new_commit_options, new_config_options, image_commit_with_options, config_with_options
|
||||
};
|
||||
|
||||
// Re-export nerdctl functions
|
||||
pub use nerdctl::register_nerdctl_module;
|
||||
pub use nerdctl::{
|
||||
nerdctl_run, nerdctl_exec,
|
||||
nerdctl_copy, nerdctl_stop, nerdctl_remove, nerdctl_list
|
||||
};
|
||||
|
||||
// Re-export git functions
|
||||
pub use git::register_git_module;
|
||||
pub use git::{
|
||||
git_clone, git_list, git_update, git_update_force, git_update_commit,
|
||||
git_update_commit_push, has_git_changes, find_matching_repos
|
||||
};
|
||||
|
||||
// Rename copy functions to avoid conflicts
|
||||
pub use os::copy as os_copy;
|
||||
|
||||
@@ -67,6 +90,12 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
// Register Buildah module functions
|
||||
buildah::register_bah_module(engine)?;
|
||||
|
||||
// Register Nerdctl module functions
|
||||
nerdctl::register_nerdctl_module(engine)?;
|
||||
|
||||
// Register Git module functions
|
||||
git::register_git_module(engine)?;
|
||||
|
||||
// Future modules can be registered here
|
||||
|
||||
Ok(())
|
||||
|
188
src/rhai/nerdctl.rs
Normal file
188
src/rhai/nerdctl.rs
Normal file
@@ -0,0 +1,188 @@
|
||||
//! Rhai wrappers for Nerdctl module functions
|
||||
//!
|
||||
//! This module provides Rhai wrappers for the functions in the Nerdctl module.
|
||||
|
||||
use rhai::{Engine, EvalAltResult, Array, Dynamic, Map};
|
||||
use crate::virt::nerdctl::{self, NerdctlError, Image};
|
||||
use crate::process::CommandResult;
|
||||
|
||||
/// Register Nerdctl module functions with the Rhai engine
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `engine` - The Rhai engine to register the functions with
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
|
||||
pub fn register_nerdctl_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
||||
// Register types
|
||||
register_nerdctl_types(engine)?;
|
||||
|
||||
// Register container functions
|
||||
engine.register_fn("nerdctl_run", nerdctl_run);
|
||||
engine.register_fn("nerdctl_run_with_name", nerdctl_run_with_name);
|
||||
engine.register_fn("nerdctl_run_with_port", nerdctl_run_with_port);
|
||||
engine.register_fn("new_run_options", new_run_options);
|
||||
engine.register_fn("nerdctl_exec", nerdctl_exec);
|
||||
engine.register_fn("nerdctl_copy", nerdctl_copy);
|
||||
engine.register_fn("nerdctl_stop", nerdctl_stop);
|
||||
engine.register_fn("nerdctl_remove", nerdctl_remove);
|
||||
engine.register_fn("nerdctl_list", nerdctl_list);
|
||||
|
||||
// Register image functions
|
||||
engine.register_fn("nerdctl_images", nerdctl_images);
|
||||
engine.register_fn("nerdctl_image_remove", nerdctl_image_remove);
|
||||
engine.register_fn("nerdctl_image_push", nerdctl_image_push);
|
||||
engine.register_fn("nerdctl_image_tag", nerdctl_image_tag);
|
||||
engine.register_fn("nerdctl_image_pull", nerdctl_image_pull);
|
||||
engine.register_fn("nerdctl_image_commit", nerdctl_image_commit);
|
||||
engine.register_fn("nerdctl_image_build", nerdctl_image_build);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register Nerdctl module types with the Rhai engine
|
||||
fn register_nerdctl_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
||||
// Register Image type and methods
|
||||
engine.register_type_with_name::<Image>("NerdctlImage");
|
||||
|
||||
// Register getters for Image properties
|
||||
engine.register_get("id", |img: &mut Image| img.id.clone());
|
||||
engine.register_get("repository", |img: &mut Image| img.repository.clone());
|
||||
engine.register_get("tag", |img: &mut Image| img.tag.clone());
|
||||
engine.register_get("size", |img: &mut Image| img.size.clone());
|
||||
engine.register_get("created", |img: &mut Image| img.created.clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Helper functions for error conversion
|
||||
fn nerdctl_error_to_rhai_error<T>(result: Result<T, NerdctlError>) -> Result<T, Box<EvalAltResult>> {
|
||||
result.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Nerdctl error: {}", e).into(),
|
||||
rhai::Position::NONE
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new Map with default run options
|
||||
pub fn new_run_options() -> Map {
|
||||
let mut map = Map::new();
|
||||
map.insert("name".into(), Dynamic::UNIT);
|
||||
map.insert("detach".into(), Dynamic::from(true));
|
||||
map.insert("ports".into(), Dynamic::from(Array::new()));
|
||||
map.insert("snapshotter".into(), Dynamic::from("native"));
|
||||
map
|
||||
}
|
||||
|
||||
//
|
||||
// Container Function Wrappers
|
||||
//
|
||||
|
||||
/// Wrapper for nerdctl::run
|
||||
///
|
||||
/// Run a container from an image.
|
||||
pub fn nerdctl_run(image: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::run(image, None, true, None, None))
|
||||
}
|
||||
|
||||
/// Run a container with a name
|
||||
pub fn nerdctl_run_with_name(image: &str, name: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::run(image, Some(name), true, None, None))
|
||||
}
|
||||
|
||||
/// Run a container with a port mapping
|
||||
pub fn nerdctl_run_with_port(image: &str, name: &str, port: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
let ports = vec![port];
|
||||
nerdctl_error_to_rhai_error(nerdctl::run(image, Some(name), true, Some(&ports), None))
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::exec
|
||||
///
|
||||
/// Execute a command in a container.
|
||||
pub fn nerdctl_exec(container: &str, command: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::exec(container, command))
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::copy
|
||||
///
|
||||
/// Copy files between container and local filesystem.
|
||||
pub fn nerdctl_copy(source: &str, dest: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::copy(source, dest))
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::stop
|
||||
///
|
||||
/// Stop a container.
|
||||
pub fn nerdctl_stop(container: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::stop(container))
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::remove
|
||||
///
|
||||
/// Remove a container.
|
||||
pub fn nerdctl_remove(container: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::remove(container))
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::list
|
||||
///
|
||||
/// List containers.
|
||||
pub fn nerdctl_list(all: bool) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::list(all))
|
||||
}
|
||||
|
||||
//
|
||||
// Image Function Wrappers
|
||||
//
|
||||
|
||||
/// Wrapper for nerdctl::images
|
||||
///
|
||||
/// List images in local storage.
|
||||
pub fn nerdctl_images() -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::images())
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::image_remove
|
||||
///
|
||||
/// Remove one or more images.
|
||||
pub fn nerdctl_image_remove(image: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::image_remove(image))
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::image_push
|
||||
///
|
||||
/// Push an image to a registry.
|
||||
pub fn nerdctl_image_push(image: &str, destination: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::image_push(image, destination))
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::image_tag
|
||||
///
|
||||
/// Add an additional name to a local image.
|
||||
pub fn nerdctl_image_tag(image: &str, new_name: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::image_tag(image, new_name))
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::image_pull
|
||||
///
|
||||
/// Pull an image from a registry.
|
||||
pub fn nerdctl_image_pull(image: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::image_pull(image))
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::image_commit
|
||||
///
|
||||
/// Commit a container to an image.
|
||||
pub fn nerdctl_image_commit(container: &str, image_name: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::image_commit(container, image_name))
|
||||
}
|
||||
|
||||
/// Wrapper for nerdctl::image_build
|
||||
///
|
||||
/// Build an image using a Dockerfile.
|
||||
pub fn nerdctl_image_build(tag: &str, context_path: &str) -> Result<CommandResult, Box<EvalAltResult>> {
|
||||
nerdctl_error_to_rhai_error(nerdctl::image_build(tag, context_path))
|
||||
}
|
@@ -205,4 +205,50 @@ mod tests {
|
||||
let result = engine.eval::<bool>(script).unwrap();
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
// Git Module Tests
|
||||
|
||||
#[test]
|
||||
fn test_git_module_registration() {
|
||||
let mut engine = Engine::new();
|
||||
register(&mut engine).unwrap();
|
||||
|
||||
// Test that git functions are registered
|
||||
let script = r#"
|
||||
// Check if git_clone function exists
|
||||
let fn_exists = is_def_fn("git_clone");
|
||||
fn_exists
|
||||
"#;
|
||||
|
||||
let result = engine.eval::<bool>(script).unwrap();
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_git_parse_url() {
|
||||
let mut engine = Engine::new();
|
||||
register(&mut engine).unwrap();
|
||||
|
||||
// Test parsing a git URL
|
||||
let script = r#"
|
||||
// We can't directly test git_clone without actually cloning,
|
||||
// but we can test that the function exists and doesn't error
|
||||
// when called with invalid parameters
|
||||
|
||||
let result = false;
|
||||
|
||||
try {
|
||||
// This should fail but not crash
|
||||
git_clone("invalid-url");
|
||||
} catch(err) {
|
||||
// Expected error
|
||||
result = err.contains("Git error");
|
||||
}
|
||||
|
||||
result
|
||||
"#;
|
||||
|
||||
let result = engine.eval::<bool>(script).unwrap();
|
||||
assert!(result);
|
||||
}
|
||||
}
|
131
src/rhai/tests/git_test.rhai
Normal file
131
src/rhai/tests/git_test.rhai
Normal file
@@ -0,0 +1,131 @@
|
||||
// Test script for Git module functions
|
||||
|
||||
// Import required modules
|
||||
import "os" as os;
|
||||
import "process" as process;
|
||||
|
||||
// Test git_clone function
|
||||
fn test_git_clone() {
|
||||
// Use a public repository for testing
|
||||
let repo_url = "https://github.com/rhaiscript/rhai.git";
|
||||
|
||||
// Clone the repository
|
||||
print("Testing git_clone...");
|
||||
let result = git_clone(repo_url);
|
||||
|
||||
// Print the result
|
||||
print(`Clone result: ${result}`);
|
||||
|
||||
// Verify the repository exists
|
||||
if result.contains("already exists") {
|
||||
print("Repository already exists, test passed");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the path exists
|
||||
if exist(result) {
|
||||
print("Repository cloned successfully, test passed");
|
||||
return true;
|
||||
}
|
||||
|
||||
print("Repository clone failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test git_list function
|
||||
fn test_git_list() {
|
||||
print("Testing git_list...");
|
||||
let repos = git_list();
|
||||
|
||||
print(`Found ${repos.len()} repositories`);
|
||||
|
||||
// Print the first few repositories
|
||||
let count = if repos.len() > 3 { 3 } else { repos.len() };
|
||||
for i in range(0, count) {
|
||||
print(` - ${repos[i]}`);
|
||||
}
|
||||
|
||||
return repos.len() > 0;
|
||||
}
|
||||
|
||||
// Test git_has_changes function
|
||||
fn test_git_has_changes() {
|
||||
print("Testing git_has_changes...");
|
||||
|
||||
// Get a repository from the list
|
||||
let repos = git_list();
|
||||
if repos.len() == 0 {
|
||||
print("No repositories found, skipping test");
|
||||
return true;
|
||||
}
|
||||
|
||||
let repo = repos[0];
|
||||
let has_changes = git_has_changes(repo);
|
||||
|
||||
print(`Repository ${repo} has changes: ${has_changes}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test find_matching_repos function
|
||||
fn test_find_matching_repos() {
|
||||
print("Testing find_matching_repos...");
|
||||
|
||||
// Get all repositories with wildcard
|
||||
let all_repos = git_list();
|
||||
if all_repos.len() == 0 {
|
||||
print("No repositories found, skipping test");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Extract a part of the first repo name to search for
|
||||
let repo_name = all_repos[0].split("/").last();
|
||||
let search_pattern = repo_name.substring(0, 3) + "*";
|
||||
|
||||
print(`Searching for repositories matching pattern: ${search_pattern}`);
|
||||
let matching = find_matching_repos(search_pattern);
|
||||
|
||||
print(`Found ${matching.len()} matching repositories`);
|
||||
for repo in matching {
|
||||
print(` - ${repo}`);
|
||||
}
|
||||
|
||||
return matching.len() > 0;
|
||||
}
|
||||
|
||||
// Run the tests
|
||||
fn run_tests() {
|
||||
let tests = [
|
||||
#{ name: "git_clone", fn: test_git_clone },
|
||||
#{ name: "git_list", fn: test_git_list },
|
||||
#{ name: "git_has_changes", fn: test_git_has_changes },
|
||||
#{ name: "find_matching_repos", fn: test_find_matching_repos }
|
||||
];
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
for test in tests {
|
||||
print(`\nRunning test: ${test.name}`);
|
||||
|
||||
let result = false;
|
||||
try {
|
||||
result = test.fn();
|
||||
} catch(err) {
|
||||
print(`Test ${test.name} threw an error: ${err}`);
|
||||
result = false;
|
||||
}
|
||||
|
||||
if result {
|
||||
print(`Test ${test.name} PASSED`);
|
||||
passed += 1;
|
||||
} else {
|
||||
print(`Test ${test.name} FAILED`);
|
||||
failed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
print(`\nTest summary: ${passed} passed, ${failed} failed`);
|
||||
}
|
||||
|
||||
// Run all tests
|
||||
run_tests();
|
Reference in New Issue
Block a user