diff --git a/examples/buildah.rs b/examples/buildah.rs index b1de7a0..211e716 100644 --- a/examples/buildah.rs +++ b/examples/buildah.rs @@ -20,13 +20,13 @@ pub fn run_buildah_example() -> Result<(), BuildahError> { // Step 2: Run a command in the container println!("\n=== Installing nginx in container ==="); // Use chroot isolation to avoid BPF issues - let install_result = buildah::run_with_isolation(container_id, "dnf install -y nginx", "chroot")?; + let install_result = buildah::bah_run_with_isolation(container_id, "dnf install -y nginx", "chroot")?; println!("{:#?}", install_result); println!("Installation output: {}", install_result.stdout); // Step 3: Copy a file into the container println!("\n=== Copying configuration file to container ==="); - buildah::copy(container_id, "./example.conf", "/etc/example.conf").unwrap(); + buildah::bah_copy(container_id, "./example.conf", "/etc/example.conf").unwrap(); // Step 4: Configure container metadata println!("\n=== Configuring container metadata ==="); @@ -34,7 +34,7 @@ pub fn run_buildah_example() -> Result<(), BuildahError> { config_options.insert("port".to_string(), "80".to_string()); config_options.insert("label".to_string(), "maintainer=example@example.com".to_string()); config_options.insert("entrypoint".to_string(), "/usr/sbin/nginx".to_string()); - + buildah::bah_config(container_id, config_options)?; buildah::config(container_id, config_options)?; println!("Container configured"); @@ -69,7 +69,7 @@ pub fn build_image_example() -> Result<(), BuildahError> { println!("Building an image from a Containerfile..."); // Use the build function with tag, context directory, and isolation to avoid BPF issues - let result = buildah::build(Some("my-app:latest"), ".", "example_Dockerfile", Some("chroot"))?; + let result = buildah::bah_build(Some("my-app:latest"), ".", "example_Dockerfile", Some("chroot"))?; println!("Build output: {}", result.stdout); println!("Image built successfully!"); diff --git a/examples/rhai_example.rs b/examples/rhai_example.rs index 8478c6f..0f4d292 100644 --- a/examples/rhai_example.rs +++ b/examples/rhai_example.rs @@ -2,8 +2,6 @@ //! //! This example demonstrates how to use the Rhai scripting language //! with the System Abstraction Layer (SAL) library. -use rhai::Engine; -use sal::rhai; use sal::rhai::{self, Engine}; use std::fs; diff --git a/rhaiexamples/06_file_read_write.rhai b/rhaiexamples/06_file_read_write.rhai index 1ddbbf5..9eca85d 100644 --- a/rhaiexamples/06_file_read_write.rhai +++ b/rhaiexamples/06_file_read_write.rhai @@ -44,11 +44,8 @@ println(appended_content); // 4. Demonstrate multiple appends println(`\n--- Demonstrating multiple appends ---`); for i in range(1, 4) { - // Get timestamp and ensure it's properly trimmed - let result = run("date"); - let timestamp = result.stdout.trim(); - println(`Timestamp: ${timestamp}`); - let log_entry = `Log entry #${i} at ${timestamp}\n`; + // Use a simple counter instead of timestamp to avoid issues + let log_entry = `Log entry #${i} - appended at iteration ${i}\n`; file_write_append(append_file, log_entry); println(`Added log entry #${i}`); } diff --git a/rhaiexamples/07_nerdctl_operations.rhai b/rhaiexamples/07_nerdctl_operations.rhai new file mode 100644 index 0000000..e3ac8cc --- /dev/null +++ b/rhaiexamples/07_nerdctl_operations.rhai @@ -0,0 +1,100 @@ +// 07_nerdctl_operations.rhai +// Demonstrates container operations using SAL's nerdctl integration +// Note: This script requires nerdctl to be installed and may need root privileges + +// Check if nerdctl is installed +let nerdctl_exists = which("nerdctl"); +println(`Nerdctl exists: ${nerdctl_exists}`); + +// List available images (only if nerdctl is installed) +println("Listing available container images:"); +if nerdctl_exists == "" { + println("Nerdctl is not installed. Please install it first."); + // You can use the install_nerdctl.rhai script to install nerdctl + // EXIT +} + +// List images +let images_result = nerdctl_images(); +println(`Images result: success=${images_result.success}, code=${images_result.code}`); +println(`Images output:\n${images_result.stdout}`); + +// Pull an image if needed +println("\nPulling alpine:latest image:"); +let pull_result = nerdctl_image_pull("alpine:latest"); +println(`Pull result: success=${pull_result.success}, code=${pull_result.code}`); +println(`Pull output: ${pull_result.stdout}`); + +// Create a container using the simple run function +println("\nCreating a container from alpine image:"); +let container_name = "rhai-nerdctl-test"; +let container = nerdctl_run("alpine:latest", container_name); +println(`Container result: success=${container.success}, code=${container.code}`); +println(`Container stdout: "${container.stdout}"`); +println(`Container stderr: "${container.stderr}"`); + +// Run a command in the container +println("\nRunning a command in the container:"); +let run_result = nerdctl_exec(container_name, "echo 'Hello from container'"); +println(`Command output: ${run_result.stdout}`); + +// Create a test file and copy it to the container +println("\nAdding a file to the container:"); +let test_file = "test_file.txt"; +run(`echo "Test content" > ${test_file}`); +let copy_result = nerdctl_copy(test_file, `${container_name}:/`); +println(`Copy result: ${copy_result.success}`); + +// Commit the container to create a new image +println("\nCommitting the container to create a new image:"); +let image_name = "my-custom-alpine:latest"; +let commit_result = nerdctl_image_commit(container_name, image_name); +println(`Commit result: ${commit_result.success}`); + +// Stop and remove the container +println("\nStopping the container:"); +let stop_result = nerdctl_stop(container_name); +println(`Stop result: ${stop_result.success}`); + +println("\nRemoving the container:"); +let remove_result = nerdctl_remove(container_name); +println(`Remove result: ${remove_result.success}`); + +// Clean up the test file +delete(test_file); + +// Demonstrate run options +println("\nDemonstrating run options:"); +let run_options = nerdctl_new_run_options(); +run_options.name = "rhai-nerdctl-options-test"; +run_options.detach = true; +run_options.ports = ["8080:80"]; +run_options.snapshotter = "native"; + +println("Run options configured:"); +println(` - Name: ${run_options.name}`); +println(` - Detach: ${run_options.detach}`); +println(` - Ports: ${run_options.ports}`); +println(` - Snapshotter: ${run_options.snapshotter}`); + +// Create a container with options +println("\nCreating a container with options:"); +let container_with_options = nerdctl_run_with_options("alpine:latest", run_options); +println(`Container with options result: ${container_with_options.success}`); + +// Clean up the container with options +println("\nCleaning up the container with options:"); +nerdctl_stop("rhai-nerdctl-options-test"); +nerdctl_remove("rhai-nerdctl-options-test"); + +// List all containers (including stopped ones) +println("\nListing all containers:"); +let list_result = nerdctl_list(true); +println(`List result: ${list_result.stdout}`); + +// Remove the custom image +println("\nRemoving the custom image:"); +let image_remove_result = nerdctl_image_remove(image_name); +println(`Image remove result: ${image_remove_result.success}`); + +"Nerdctl operations script completed successfully!" \ No newline at end of file diff --git a/rhaiexamples/08_nerdctl_web_server.rhai b/rhaiexamples/08_nerdctl_web_server.rhai new file mode 100644 index 0000000..efe1344 --- /dev/null +++ b/rhaiexamples/08_nerdctl_web_server.rhai @@ -0,0 +1,150 @@ +// 08_nerdctl_web_server.rhai +// Demonstrates a complete workflow to set up a web server using nerdctl +// Note: This script requires nerdctl to be installed and may need root privileges + +// Check if nerdctl is installed +let nerdctl_exists = which("nerdctl"); +if nerdctl_exists == "" { + println("Nerdctl is not installed. Please install it first."); + // You can use the install_nerdctl.rhai script to install nerdctl + // EXIT +} + +println("Starting nerdctl web server workflow..."); + +// Step 1: Pull the nginx image +println("\n=== Pulling nginx:latest image ==="); +let pull_result = nerdctl_image_pull("nginx:latest"); +if !pull_result.success { + println(`Failed to pull nginx image: ${pull_result.stderr}`); + // EXIT +} +println("Successfully pulled nginx:latest image"); + +// Step 2: Create a custom nginx configuration file +println("\n=== Creating custom nginx configuration ==="); +let config_content = ` +server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html; + } +} +`; + +let config_file = "custom-nginx.conf"; +run(`echo "${config_content}" > ${config_file}`); +println("Created custom nginx configuration file"); + +// Step 3: Create a custom index.html file +println("\n=== Creating custom index.html ==="); +let html_content = ` + + + + Rhai Nerdctl Demo + + + +

Hello from Rhai Nerdctl!

+

This page is served by an Nginx container created using the Rhai nerdctl wrapper.

+

Current time: ${now()}

+ + +`; + +let html_file = "index.html"; +run(`echo "${html_content}" > ${html_file}`); +println("Created custom index.html file"); + +// Step 4: Create and run the nginx container with options +println("\n=== Creating nginx container ==="); +let container_name = "rhai-nginx-demo"; + +// First, try to remove any existing container with the same name +let _ = nerdctl_remove(container_name); + +let run_options = nerdctl_new_run_options(); +run_options.name = container_name; +run_options.detach = true; +run_options.ports = ["8080:80"]; +run_options.snapshotter = "native"; + +let container = nerdctl_run_with_options("nginx:latest", run_options); +if !container.success { + println(`Failed to create container: ${container.stderr}`); + // EXIT +} +println(`Successfully created container: ${container_name}`); + +// Step 5: Copy the custom files to the container +println("\n=== Copying custom files to container ==="); +let copy_config = nerdctl_copy(config_file, `${container_name}:/etc/nginx/conf.d/default.conf`); +if !copy_config.success { + println(`Failed to copy config file: ${copy_config.stderr}`); +} + +let copy_html = nerdctl_copy(html_file, `${container_name}:/usr/share/nginx/html/index.html`); +if !copy_html.success { + println(`Failed to copy HTML file: ${copy_html.stderr}`); +} +println("Successfully copied custom files to container"); + +// Step 6: Restart the container to apply changes +println("\n=== Restarting container to apply changes ==="); +let stop_result = nerdctl_stop(container_name); +if !stop_result.success { + println(`Failed to stop container: ${stop_result.stderr}`); +} + +let start_result = nerdctl_exec(container_name, "nginx -s reload"); +if !start_result.success { + println(`Failed to reload nginx: ${start_result.stderr}`); +} +println("Successfully restarted container"); + +// Step 7: Commit the container to create a custom image +println("\n=== Committing container to create custom image ==="); +let image_name = "rhai-nginx-custom:latest"; +let commit_result = nerdctl_image_commit(container_name, image_name); +if !commit_result.success { + println(`Failed to commit container: ${commit_result.stderr}`); +} +println(`Successfully created custom image: ${image_name}`); + +// Step 8: Display information about the running container +println("\n=== Container Information ==="); +println("The nginx web server is now running."); +println("You can access it at: http://localhost:8080"); +println("Container name: " + container_name); +println("Custom image: " + image_name); + +// Step 9: Clean up (commented out for demonstration purposes) +// println("\n=== Cleaning up ==="); +// nerdctl_stop(container_name); +// nerdctl_remove(container_name); +// nerdctl_image_remove(image_name); +// delete(config_file); +// delete(html_file); + +println("\nNerdctl web server workflow completed successfully!"); +println("The web server is running at http://localhost:8080"); +println("To clean up, run the following commands:"); +println(` nerdctl stop ${container_name}`); +println(` nerdctl rm ${container_name}`); +println(` nerdctl rmi ${image_name}`); + +"Nerdctl web server script completed successfully!" \ No newline at end of file diff --git a/rhaiexamples/nerdctl_test.rhai b/rhaiexamples/nerdctl_test.rhai new file mode 100644 index 0000000..0295862 --- /dev/null +++ b/rhaiexamples/nerdctl_test.rhai @@ -0,0 +1,66 @@ +// nerdctl_test.rhai +// Tests the nerdctl wrapper functionality without requiring a running containerd daemon + +// Check if nerdctl is installed +let nerdctl_exists = which("nerdctl"); +println(`Nerdctl exists: ${nerdctl_exists}`); + +// Test creating run options +println("\nTesting run options creation:"); +let run_options = new_run_options(); +println(`Default run options created: ${run_options}`); +println(`- name: ${run_options.name}`); +println(`- detach: ${run_options.detach}`); +println(`- ports: ${run_options.ports}`); +println(`- snapshotter: ${run_options.snapshotter}`); + +// Modify run options +println("\nModifying run options:"); +run_options.name = "test-container"; +run_options.detach = false; +run_options.ports = ["8080:80", "8443:443"]; +run_options.snapshotter = "overlayfs"; +println(`Modified run options: ${run_options}`); +println(`- name: ${run_options.name}`); +println(`- detach: ${run_options.detach}`); +println(`- ports: ${run_options.ports}`); +println(`- snapshotter: ${run_options.snapshotter}`); + +// Test function availability +println("\nTesting function availability:"); +let functions = [ + "nerdctl_run", + "nerdctl_run_with_name", + "nerdctl_run_with_port", + "nerdctl_exec", + "nerdctl_copy", + "nerdctl_stop", + "nerdctl_remove", + "nerdctl_list", + "nerdctl_images", + "nerdctl_image_remove", + "nerdctl_image_push", + "nerdctl_image_tag", + "nerdctl_image_pull", + "nerdctl_image_commit", + "nerdctl_image_build" +]; + +// Try to access each function (this will throw an error if the function doesn't exist) +for func in functions { + let exists = is_function_registered(func); + println(`Function ${func} registered: ${exists}`); +} + +// Helper function to check if a function is registered +fn is_function_registered(name) { + try { + // This will throw an error if the function doesn't exist + eval(`${name}`); + return true; + } catch { + return false; + } +} + +"Nerdctl wrapper test completed successfully!" \ No newline at end of file diff --git a/src/docs/rhai/buildah.md b/src/docs/rhai/buildah.md index 3f0f66c..666b278 100644 --- a/src/docs/rhai/buildah.md +++ b/src/docs/rhai/buildah.md @@ -1,19 +1,18 @@ # Buildah Module -The Buildah module provides functions for container and image management using the Buildah tool. Buildah is a tool for building OCI-compliant container images. +The Buildah module provides functions for working with containers and images using the Buildah tool. Buildah helps you create and manage container images. -## Types +## Image Information -### `BuildahImage` +### Image Properties -A type that represents a container image. +When working with images, you can access the following information: -**Properties:** -- `id` (string): The image ID -- `names` (array): An array of names/tags for the image -- `name` (string): The first name of the image, or `` if the image has no names -- `size` (string): The size of the image -- `created` (string): When the image was created +- `id`: The unique identifier for the image +- `names`: A list of names/tags for the image +- `name`: The primary name of the image, or `` if the image has no names +- `size`: The size of the image +- `created`: When the image was created ## Container Functions @@ -24,9 +23,10 @@ Creates a container from an image. **Parameters:** - `image` (string): The name or ID of the image to create the container from -**Returns:** A `CommandResult` object with the container ID in the stdout property, or throws an error if the operation fails. +**Returns:** The ID of the newly created container if successful. **Example:** + ```rhai // Create a container from an image let result = bah_from("alpine:latest"); @@ -42,7 +42,7 @@ Runs a command in a container. - `container` (string): The container ID or name - `command` (string): The command to run -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** The output of the command if successful. **Example:** ```rhai @@ -60,7 +60,7 @@ Runs a command in a container with specified isolation. - `command` (string): The command to run - `isolation` (string): The isolation type (e.g., "chroot", "rootless", "oci") -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** The output of the command if successful. **Example:** ```rhai @@ -78,7 +78,7 @@ Copies files into a container. - `source` (string): The source path on the host - `dest` (string): The destination path in the container -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the copy operation worked. **Example:** ```rhai @@ -95,7 +95,7 @@ Adds files into a container. Similar to `bah_copy` but can also handle remote UR - `source` (string): The source path on the host or a URL - `dest` (string): The destination path in the container -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the add operation worked. **Example:** ```rhai @@ -111,7 +111,7 @@ Commits a container to an image. - `container` (string): The container ID or name - `image_name` (string): The name to give the new image -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the commit operation worked. **Example:** ```rhai @@ -126,7 +126,7 @@ Removes a container. **Parameters:** - `container` (string): The container ID or name -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the container was removed. **Example:** ```rhai @@ -138,7 +138,7 @@ bah_remove("my-container"); Lists containers. -**Returns:** A `CommandResult` object with the list of containers in the stdout property, or throws an error if the operation fails. +**Returns:** A list of containers if successful. **Example:** ```rhai @@ -170,7 +170,7 @@ Builds an image with options specified in a map. **Parameters:** - `options` (map): A map of options created with `bah_new_build_options()` -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the build operation worked. **Example:** ```rhai @@ -191,7 +191,7 @@ let result = bah_build(options); Lists images in local storage. -**Returns:** An array of `BuildahImage` objects or throws an error if the operation fails. +**Returns:** A list of images if successful. **Example:** ```rhai @@ -204,6 +204,7 @@ for image in images { } ``` + ### `bah_image_remove(image)` Removes one or more images. @@ -211,7 +212,7 @@ Removes one or more images. **Parameters:** - `image` (string): The image ID or name -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the image was removed. **Example:** ```rhai @@ -228,7 +229,7 @@ Pushes an image to a registry. - `destination` (string): The destination registry/repository - `tls_verify` (boolean): Whether to verify TLS certificates -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the image was pushed. **Example:** ```rhai @@ -244,7 +245,7 @@ Adds an additional name to a local image. - `image` (string): The image ID or name - `new_name` (string): The new name to add -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the image was tagged. **Example:** ```rhai @@ -260,7 +261,7 @@ Pulls an image from a registry. - `image` (string): The image to pull - `tls_verify` (boolean): Whether to verify TLS certificates -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the image was pulled. **Example:** ```rhai @@ -292,7 +293,7 @@ Commits a container to an image with options specified in a map. - `image_name` (string): The name to give the new image - `options` (map): A map of options created with `bah_new_commit_options()` -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the image was created. **Example:** ```rhai @@ -326,7 +327,7 @@ Configures a container with options specified in a map. - `container` (string): The container ID or name - `options` (map): A map of options created with `bah_new_config_options()` -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** A success message if the container was configured. **Example:** ```rhai @@ -340,4 +341,5 @@ options.env = "NODE_ENV=production"; options.label = "version=1.0"; // Configure a container with options -let result = bah_config("my-container", options); \ No newline at end of file +let result = bah_config("my-container", options); +``` diff --git a/src/docs/rhai/git.md b/src/docs/rhai/git.md new file mode 100644 index 0000000..0d00990 --- /dev/null +++ b/src/docs/rhai/git.md @@ -0,0 +1,113 @@ +# Git Module for Rhai + +This module provides Rhai wrappers for the Git functionality in SAL. + +## Basic Git Operations + +### Clone a Repository + +```rhai +// Clone a repository to a standardized location in the user's home directory +let repo_path = git_clone("https://github.com/username/repo.git"); +print(`Repository cloned to: ${repo_path}`); +``` + +### List Repositories + +```rhai +// List all git repositories in the user's ~/code directory +let repos = git_list(); +print("Found repositories:"); +for repo in repos { + print(` - ${repo}`); +} +``` + +### Find Repositories + +```rhai +// Find repositories matching a pattern +// Use a wildcard (*) suffix to find multiple matches +let matching_repos = find_matching_repos("my-project*"); +print("Matching repositories:"); +for repo in matching_repos { + print(` - ${repo}`); +} + +// Find a specific repository (must match exactly one) +let specific_repo = find_matching_repos("unique-project")[0]; +print(`Found specific repository: ${specific_repo}`); +``` + +### Check for Changes + +```rhai +// Check if a repository has uncommitted changes +let repo_path = "/path/to/repo"; +if git_has_changes(repo_path) { + print("Repository has uncommitted changes"); +} else { + print("Repository is clean"); +} +``` + +## Repository Updates + +### Update a Repository + +```rhai +// Update a repository by pulling the latest changes +// This will fail if there are uncommitted changes +let result = git_update("my-project"); +print(result); +``` + +### Force Update a Repository + +```rhai +// Force update a repository by discarding local changes and pulling the latest changes +let result = git_update_force("my-project"); +print(result); +``` + +### Commit and Update + +```rhai +// Commit changes in a repository and then update it by pulling the latest changes +let result = git_update_commit("my-project", "Fix bug in login form"); +print(result); +``` + +### Commit and Push + +```rhai +// Commit changes in a repository and push them to the remote +let result = git_update_commit_push("my-project", "Add new feature"); +print(result); +``` + + +## Complete Example + +```rhai +// Clone a repository +let repo_url = "https://github.com/username/example-repo.git"; +let repo_path = git_clone(repo_url); +print(`Cloned repository to: ${repo_path}`); + +// Make some changes (using OS module functions) +let file_path = `${repo_path}/README.md`; +let content = "# Example Repository\n\nThis is an example repository."; +write_file(file_path, content); + +// Commit and push the changes +let commit_message = "Update README.md"; +let result = git_update_commit_push(repo_path, commit_message); +print(result); + +// List all repositories +let repos = git_list(); +print("All repositories:"); +for repo in repos { + print(` - ${repo}`); +} \ No newline at end of file diff --git a/src/docs/rhai/os.md b/src/docs/rhai/os.md index 2c37851..8cc44c2 100644 --- a/src/docs/rhai/os.md +++ b/src/docs/rhai/os.md @@ -1,6 +1,6 @@ # OS Module -The OS module provides functions for file system operations, directory management, and downloading files. +The OS module provides functions for working with files, directories, and downloading files from the internet. ## File System Functions @@ -12,7 +12,7 @@ Recursively copies a file or directory from source to destination. - `src` (string): The source file or directory path - `dest` (string): The destination path -**Returns:** A string with the result message or throws an error if the operation fails. +**Returns:** A message confirming the copy was successful. **Example:** ```rhai @@ -49,7 +49,7 @@ Finds a file in a directory with support for wildcards. - `dir` (string): The directory to search in - `filename` (string): The filename pattern to search for (supports wildcards) -**Returns:** The path of the first matching file or throws an error if no file is found. +**Returns:** The path of the first matching file. **Example:** ```rhai @@ -68,7 +68,7 @@ Finds multiple files in a directory recursively with support for wildcards. - `dir` (string): The directory to search in - `filename` (string): The filename pattern to search for (supports wildcards) -**Returns:** An array of matching file paths or throws an error if the operation fails. +**Returns:** A list of matching file paths. **Example:** ```rhai @@ -89,7 +89,7 @@ Finds a directory in a parent directory with support for wildcards. - `dir` (string): The parent directory to search in - `dirname` (string): The directory name pattern to search for (supports wildcards) -**Returns:** The path of the first matching directory or throws an error if no directory is found. +**Returns:** The path of the first matching directory. **Example:** ```rhai @@ -108,7 +108,7 @@ Finds multiple directories in a parent directory recursively with support for wi - `dir` (string): The parent directory to search in - `dirname` (string): The directory name pattern to search for (supports wildcards) -**Returns:** An array of matching directory paths or throws an error if the operation fails. +**Returns:** A list of matching directory paths. **Example:** ```rhai @@ -128,7 +128,7 @@ Deletes a file or directory. This function is defensive and doesn't error if the **Parameters:** - `path` (string): The path of the file or directory to delete -**Returns:** A string with the result message or throws an error if the operation fails. +**Returns:** A message confirming the deletion was successful. **Example:** ```rhai @@ -146,7 +146,7 @@ Creates a directory and all parent directories. This function is defensive and d **Parameters:** - `path` (string): The path of the directory to create -**Returns:** A string with the result message or throws an error if the operation fails. +**Returns:** A message confirming the directory was created. **Example:** ```rhai @@ -164,7 +164,7 @@ Gets the size of a file in bytes. **Parameters:** - `path` (string): The path of the file -**Returns:** The size of the file in bytes (as an integer) or throws an error if the operation fails. +**Returns:** The size of the file in bytes. **Example:** ```rhai @@ -182,7 +182,7 @@ Reads the contents of a file. **Parameters:** - `path` (string): The path of the file to read -**Returns:** The content of the file as a string or throws an error if the operation fails. +**Returns:** The content of the file as a string. **Example:** ```rhai @@ -199,7 +199,7 @@ Writes content to a file. Creates the file if it doesn't exist, overwrites if it - `path` (string): The path of the file to write to - `content` (string): The content to write to the file -**Returns:** A string with the result message or throws an error if the operation fails. +**Returns:** A message confirming the file was written. **Example:** ```rhai @@ -215,7 +215,7 @@ Appends content to a file. Creates the file if it doesn't exist. - `path` (string): The path of the file to append to - `content` (string): The content to append to the file -**Returns:** A string with the result message or throws an error if the operation fails. +**Returns:** A message confirming the content was appended. **Example:** ```rhai @@ -231,7 +231,7 @@ Syncs directories using rsync (or platform equivalent). - `src` (string): The source directory - `dest` (string): The destination directory -**Returns:** A string with the result message or throws an error if the operation fails. +**Returns:** A message confirming the directories were synced. **Example:** ```rhai @@ -246,7 +246,7 @@ Changes the current working directory. **Parameters:** - `path` (string): The path to change to -**Returns:** A string with the result message or throws an error if the operation fails. +**Returns:** A message confirming the directory was changed. **Example:** ```rhai @@ -271,7 +271,7 @@ Downloads a file from a URL to a destination using the curl command. If the URL - `dest` (string): The destination path to save the file - `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation) -**Returns:** A string with the result message or throws an error if the operation fails. +**Returns:** The path where the file was saved or extracted. **Example:** ```rhai @@ -290,7 +290,7 @@ Downloads a file and installs it if it's a supported package format. - `url` (string): The URL to download from - `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation) -**Returns:** A string with the result message or throws an error if the operation fails. +**Returns:** The path where the file was saved or installed. **Example:** ```rhai diff --git a/src/docs/rhai/process.md b/src/docs/rhai/process.md index 88362a0..4208928 100644 --- a/src/docs/rhai/process.md +++ b/src/docs/rhai/process.md @@ -1,6 +1,26 @@ # Process Module -The Process module provides functions for executing commands and managing processes on the system. +The Process module provides functions for running commands and managing processes on your system. + +## Command Results + +### Command Output Information + +When you run a command, you get back information about what happened: + +- `stdout`: The normal output of the command +- `stderr`: Any error messages from the command +- `success`: Whether the command worked (true) or failed (false) +- `code`: The exit code (0 usually means success) + +### Process Information + +When you get information about a running process, you can see: + +- `pid`: The process ID number +- `name`: The name of the process +- `memory`: How much memory the process is using +- `cpu`: How much CPU the process is using ## Run Functions @@ -11,7 +31,7 @@ Runs a command or multiline script with arguments. **Parameters:** - `command` (string): The command to run -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** The result of the command, including output and whether it succeeded. **Example:** ```rhai @@ -27,28 +47,6 @@ if result.success { ``` -## Types - -### `CommandResult` - -A type that represents the result of a command execution. - -**Properties:** -- `stdout` (string): The standard output of the command -- `stderr` (string): The standard error output of the command -- `success` (boolean): Whether the command executed successfully -- `code` (integer): The exit code of the command - -### `ProcessInfo` - -A type that represents information about a running process. - -**Properties:** -- `pid` (integer): The process ID -- `name` (string): The name of the process -- `memory` (integer): The memory usage of the process -- `cpu` (float): The CPU usage of the process - ### `run_silent(command)` @@ -57,7 +55,7 @@ Runs a command or multiline script with arguments silently (without displaying o **Parameters:** - `command` (string): The command to run -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** The result of the command, without displaying the output. **Example:** @@ -73,8 +71,6 @@ if result.code == 0 { } ``` -Note we have tools for git too, no reason to do this manual. - ### `new_run_options()` Creates a new map with default run options. @@ -99,7 +95,7 @@ Runs a command with options specified in a map. - `command` (string): The command to run - `options` (map): A map of options created with `new_run_options()` -**Returns:** A `CommandResult` object or throws an error if the operation fails. +**Returns:** The result of the command with your custom settings applied. **Example:** ```rhai @@ -123,7 +119,7 @@ Checks if a command exists in the PATH. **Parameters:** - `cmd` (string): The command to check -**Returns:** The full path to the command if found, or `()` (unit/null) if not found. +**Returns:** The full path to the command if found, or nothing if not found. **Example:** ```rhai @@ -144,7 +140,7 @@ Kills processes matching a pattern. **Parameters:** - `pattern` (string): The pattern to match process names against -**Returns:** A string with the result message or throws an error if the operation fails. +**Returns:** A message confirming the processes were killed. **Example:** ```rhai @@ -159,7 +155,7 @@ Lists processes matching a pattern (or all processes if the pattern is empty). **Parameters:** - `pattern` (string): The pattern to match process names against (can be empty to list all processes) -**Returns:** An array of `ProcessInfo` objects or throws an error if the operation fails. +**Returns:** A list of processes matching your search. **Example:** ```rhai @@ -182,7 +178,7 @@ Gets a single process matching the pattern. Throws an error if zero or more than **Parameters:** - `pattern` (string): The pattern to match process names against -**Returns:** A `ProcessInfo` object or throws an error if zero or multiple processes match. +**Returns:** Information about the matching process. This will only work if exactly one process matches. **Example:** ```rhai @@ -192,4 +188,5 @@ try { print(`Found process: PID=${process.pid}, Name=${process.name}`); } catch(err) { print(`Error: ${err}`); -} \ No newline at end of file +} +``` \ No newline at end of file diff --git a/src/examples/git_test.rs b/src/examples/git_test.rs new file mode 100644 index 0000000..d3c666c --- /dev/null +++ b/src/examples/git_test.rs @@ -0,0 +1,66 @@ +//! Example of using the Git module with Rhai +//! +//! This example demonstrates how to use the Git module functions +//! through the Rhai scripting language. + +use sal::rhai::{self, Engine}; + +fn main() -> Result<(), Box> { + // Create a new Rhai engine + let mut engine = Engine::new(); + + // Register SAL functions with the engine + rhai::register(&mut engine)?; + + // Run a Rhai script that uses Git functions + let script = r#" + // Print a header + print("=== Testing Git Module Functions ===\n"); + + // Test git_list function + print("Listing git repositories..."); + let repos = git_list(); + print(`Found ${repos.len()} repositories`); + + // Print the first few repositories + if repos.len() > 0 { + print("First few repositories:"); + let count = if repos.len() > 3 { 3 } else { repos.len() }; + for i in range(0, count) { + print(` - ${repos[i]}`); + } + } + + // Test find_matching_repos function + if repos.len() > 0 { + print("\nTesting repository search..."); + // Extract a part of the first repo name to search for + let repo_path = repos[0]; + let parts = repo_path.split("/"); + let repo_name = parts[parts.len() - 1]; + + print(`Searching for repositories containing "${repo_name}"`); + let matching = find_matching_repos(repo_name); + + print(`Found ${matching.len()} matching repositories`); + for repo in matching { + print(` - ${repo}`); + } + + // Check if a repository has changes + print("\nChecking for changes in repository..."); + let has_changes = git_has_changes(repo_path); + print(`Repository ${repo_path} has changes: ${has_changes}`); + } + + print("\n=== Git Module Test Complete ==="); + "#; + + // Evaluate the script + match engine.eval::<()>(script) { + Ok(_) => println!("Script executed successfully"), + Err(e) => eprintln!("Script execution error: {}", e), + } + + Ok(()) +} \ No newline at end of file diff --git a/src/examples/rhai_git_example.rs b/src/examples/rhai_git_example.rs new file mode 100644 index 0000000..d3c666c --- /dev/null +++ b/src/examples/rhai_git_example.rs @@ -0,0 +1,66 @@ +//! Example of using the Git module with Rhai +//! +//! This example demonstrates how to use the Git module functions +//! through the Rhai scripting language. + +use sal::rhai::{self, Engine}; + +fn main() -> Result<(), Box> { + // Create a new Rhai engine + let mut engine = Engine::new(); + + // Register SAL functions with the engine + rhai::register(&mut engine)?; + + // Run a Rhai script that uses Git functions + let script = r#" + // Print a header + print("=== Testing Git Module Functions ===\n"); + + // Test git_list function + print("Listing git repositories..."); + let repos = git_list(); + print(`Found ${repos.len()} repositories`); + + // Print the first few repositories + if repos.len() > 0 { + print("First few repositories:"); + let count = if repos.len() > 3 { 3 } else { repos.len() }; + for i in range(0, count) { + print(` - ${repos[i]}`); + } + } + + // Test find_matching_repos function + if repos.len() > 0 { + print("\nTesting repository search..."); + // Extract a part of the first repo name to search for + let repo_path = repos[0]; + let parts = repo_path.split("/"); + let repo_name = parts[parts.len() - 1]; + + print(`Searching for repositories containing "${repo_name}"`); + let matching = find_matching_repos(repo_name); + + print(`Found ${matching.len()} matching repositories`); + for repo in matching { + print(` - ${repo}`); + } + + // Check if a repository has changes + print("\nChecking for changes in repository..."); + let has_changes = git_has_changes(repo_path); + print(`Repository ${repo_path} has changes: ${has_changes}`); + } + + print("\n=== Git Module Test Complete ==="); + "#; + + // Evaluate the script + match engine.eval::<()>(script) { + Ok(_) => println!("Script executed successfully"), + Err(e) => eprintln!("Script execution error: {}", e), + } + + Ok(()) +} \ No newline at end of file diff --git a/src/rhai/git.rs b/src/rhai/git.rs new file mode 100644 index 0000000..968ac9c --- /dev/null +++ b/src/rhai/git.rs @@ -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>` - Ok if registration was successful, Err otherwise +pub fn register_git_module(engine: &mut Engine) -> Result<(), Box> { + // 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(result: Result) -> Result> { + 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> { + 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> { + let repos = git_error_to_rhai_error(git::git_list())?; + + // Convert Vec 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> { + 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> { + let repos = git_error_to_rhai_error(git::find_matching_repos(pattern))?; + + // Convert Vec 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> { + 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> { + 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> { + 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> { + git_error_to_rhai_error(git::git_update_commit_push(repo_path, message)) +} \ No newline at end of file diff --git a/src/rhai/mod.rs b/src/rhai/mod.rs index a318f62..813bedc 100644 --- a/src/rhai/mod.rs +++ b/src/rhai/mod.rs @@ -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> { // 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(()) diff --git a/src/rhai/nerdctl.rs b/src/rhai/nerdctl.rs new file mode 100644 index 0000000..5e48991 --- /dev/null +++ b/src/rhai/nerdctl.rs @@ -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>` - Ok if registration was successful, Err otherwise +pub fn register_nerdctl_module(engine: &mut Engine) -> Result<(), Box> { + // 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> { + // Register Image type and methods + engine.register_type_with_name::("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(result: Result) -> Result> { + 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> { + 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> { + 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> { + 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> { + 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> { + nerdctl_error_to_rhai_error(nerdctl::copy(source, dest)) +} + +/// Wrapper for nerdctl::stop +/// +/// Stop a container. +pub fn nerdctl_stop(container: &str) -> Result> { + nerdctl_error_to_rhai_error(nerdctl::stop(container)) +} + +/// Wrapper for nerdctl::remove +/// +/// Remove a container. +pub fn nerdctl_remove(container: &str) -> Result> { + nerdctl_error_to_rhai_error(nerdctl::remove(container)) +} + +/// Wrapper for nerdctl::list +/// +/// List containers. +pub fn nerdctl_list(all: bool) -> Result> { + 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> { + 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> { + 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> { + 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> { + 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> { + 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> { + 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> { + nerdctl_error_to_rhai_error(nerdctl::image_build(tag, context_path)) +} \ No newline at end of file diff --git a/src/rhai/tests.rs b/src/rhai/tests.rs index a91a5e0..61983d1 100644 --- a/src/rhai/tests.rs +++ b/src/rhai/tests.rs @@ -205,4 +205,50 @@ mod tests { let result = engine.eval::(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::(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::(script).unwrap(); + assert!(result); + } } \ No newline at end of file diff --git a/src/rhai/tests/git_test.rhai b/src/rhai/tests/git_test.rhai new file mode 100644 index 0000000..7eaf9c9 --- /dev/null +++ b/src/rhai/tests/git_test.rhai @@ -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(); \ No newline at end of file diff --git a/src/run_git_test.rs b/src/run_git_test.rs new file mode 100644 index 0000000..6af1caf --- /dev/null +++ b/src/run_git_test.rs @@ -0,0 +1,27 @@ +extern crate sal; + +use rhai::Engine; +use std::fs; +use std::error::Error; + +// Import the SAL library +use sal::rhai; + +fn main() -> Result<(), Box> { + // Create a new Rhai engine + let mut engine = Engine::new(); + + // Register all SAL modules with the engine + rhai::register(&mut engine)?; + + // Read the test script + let script = fs::read_to_string("test_git.rhai")?; + + // Evaluate the script + match engine.eval::<()>(&script) { + Ok(_) => println!("Script executed successfully"), + Err(e) => eprintln!("Script execution error: {}", e), + } + + Ok(()) +} \ No newline at end of file diff --git a/src/test_git.rhai b/src/test_git.rhai new file mode 100644 index 0000000..a5f556a --- /dev/null +++ b/src/test_git.rhai @@ -0,0 +1,42 @@ +// Simple test script for Git module functions + +// Print a header +print("=== Testing Git Module Functions ===\n"); + +// Test git_list function +print("Listing git repositories..."); +let repos = git_list(); +print(`Found ${repos.len()} repositories`); + +// Print the first few repositories +if repos.len() > 0 { + print("First few repositories:"); + let count = if repos.len() > 3 { 3 } else { repos.len() }; + for i in range(0, count) { + print(` - ${repos[i]}`); + } +} + +// Test find_matching_repos function +if repos.len() > 0 { + print("\nTesting repository search..."); + // Extract a part of the first repo name to search for + let repo_path = repos[0]; + let parts = repo_path.split("/"); + let repo_name = parts[parts.len() - 1]; + + print(`Searching for repositories containing "${repo_name}"`); + let matching = find_matching_repos(repo_name); + + print(`Found ${matching.len()} matching repositories`); + for repo in matching { + print(` - ${repo}`); + } + + // Check if a repository has changes + print("\nChecking for changes in repository..."); + let has_changes = git_has_changes(repo_path); + print(`Repository ${repo_path} has changes: ${has_changes}`); +} + +print("\n=== Git Module Test Complete ==="); \ No newline at end of file