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