This commit is contained in:
despiegk 2025-04-04 21:51:31 +02:00
parent 5b006ff328
commit 9f33c94020
19 changed files with 1221 additions and 88 deletions

View File

@ -20,13 +20,13 @@ pub fn run_buildah_example() -> Result<(), BuildahError> {
// Step 2: Run a command in the container // Step 2: Run a command in the container
println!("\n=== Installing nginx in container ==="); println!("\n=== Installing nginx in container ===");
// Use chroot isolation to avoid BPF issues // 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!("{:#?}", install_result);
println!("Installation output: {}", install_result.stdout); println!("Installation output: {}", install_result.stdout);
// Step 3: Copy a file into the container // Step 3: Copy a file into the container
println!("\n=== Copying configuration file to 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 // Step 4: Configure container metadata
println!("\n=== Configuring 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("port".to_string(), "80".to_string());
config_options.insert("label".to_string(), "maintainer=example@example.com".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()); config_options.insert("entrypoint".to_string(), "/usr/sbin/nginx".to_string());
buildah::bah_config(container_id, config_options)?;
buildah::config(container_id, config_options)?; buildah::config(container_id, config_options)?;
println!("Container configured"); println!("Container configured");
@ -69,7 +69,7 @@ pub fn build_image_example() -> Result<(), BuildahError> {
println!("Building an image from a Containerfile..."); println!("Building an image from a Containerfile...");
// Use the build function with tag, context directory, and isolation to avoid BPF issues // 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!("Build output: {}", result.stdout);
println!("Image built successfully!"); println!("Image built successfully!");

View File

@ -2,8 +2,6 @@
//! //!
//! This example demonstrates how to use the Rhai scripting language //! This example demonstrates how to use the Rhai scripting language
//! with the System Abstraction Layer (SAL) library. //! with the System Abstraction Layer (SAL) library.
use rhai::Engine;
use sal::rhai;
use sal::rhai::{self, Engine}; use sal::rhai::{self, Engine};
use std::fs; use std::fs;

View File

@ -44,11 +44,8 @@ println(appended_content);
// 4. Demonstrate multiple appends // 4. Demonstrate multiple appends
println(`\n--- Demonstrating multiple appends ---`); println(`\n--- Demonstrating multiple appends ---`);
for i in range(1, 4) { for i in range(1, 4) {
// Get timestamp and ensure it's properly trimmed // Use a simple counter instead of timestamp to avoid issues
let result = run("date"); let log_entry = `Log entry #${i} - appended at iteration ${i}\n`;
let timestamp = result.stdout.trim();
println(`Timestamp: ${timestamp}`);
let log_entry = `Log entry #${i} at ${timestamp}\n`;
file_write_append(append_file, log_entry); file_write_append(append_file, log_entry);
println(`Added log entry #${i}`); println(`Added log entry #${i}`);
} }

View File

@ -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!"

View File

@ -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 = `
<!DOCTYPE html>
<html>
<head>
<title>Rhai Nerdctl Demo</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
line-height: 1.6;
color: #333;
}
h1 {
color: #0066cc;
}
</style>
</head>
<body>
<h1>Hello from Rhai Nerdctl!</h1>
<p>This page is served by an Nginx container created using the Rhai nerdctl wrapper.</p>
<p>Current time: ${now()}</p>
</body>
</html>
`;
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!"

View File

@ -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!"

View File

@ -1,19 +1,18 @@
# Buildah Module # 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`: The unique identifier for the image
- `id` (string): The image ID - `names`: A list of names/tags for the image
- `names` (array): An array of names/tags for the image - `name`: The primary name of the image, or `<none>` if the image has no names
- `name` (string): The first name of the image, or `<none>` if the image has no names - `size`: The size of the image
- `size` (string): The size of the image - `created`: When the image was created
- `created` (string): When the image was created
## Container Functions ## Container Functions
@ -24,9 +23,10 @@ Creates a container from an image.
**Parameters:** **Parameters:**
- `image` (string): The name or ID of the image to create the container from - `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:** **Example:**
```rhai ```rhai
// Create a container from an image // Create a container from an image
let result = bah_from("alpine:latest"); let result = bah_from("alpine:latest");
@ -42,7 +42,7 @@ Runs a command in a container.
- `container` (string): The container ID or name - `container` (string): The container ID or name
- `command` (string): The command to run - `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:** **Example:**
```rhai ```rhai
@ -60,7 +60,7 @@ Runs a command in a container with specified isolation.
- `command` (string): The command to run - `command` (string): The command to run
- `isolation` (string): The isolation type (e.g., "chroot", "rootless", "oci") - `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:** **Example:**
```rhai ```rhai
@ -78,7 +78,7 @@ Copies files into a container.
- `source` (string): The source path on the host - `source` (string): The source path on the host
- `dest` (string): The destination path in the container - `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:** **Example:**
```rhai ```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 - `source` (string): The source path on the host or a URL
- `dest` (string): The destination path in the container - `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:** **Example:**
```rhai ```rhai
@ -111,7 +111,7 @@ Commits a container to an image.
- `container` (string): The container ID or name - `container` (string): The container ID or name
- `image_name` (string): The name to give the new image - `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:** **Example:**
```rhai ```rhai
@ -126,7 +126,7 @@ Removes a container.
**Parameters:** **Parameters:**
- `container` (string): The container ID or name - `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:** **Example:**
```rhai ```rhai
@ -138,7 +138,7 @@ bah_remove("my-container");
Lists containers. 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:** **Example:**
```rhai ```rhai
@ -170,7 +170,7 @@ Builds an image with options specified in a map.
**Parameters:** **Parameters:**
- `options` (map): A map of options created with `bah_new_build_options()` - `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:** **Example:**
```rhai ```rhai
@ -191,7 +191,7 @@ let result = bah_build(options);
Lists images in local storage. 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:** **Example:**
```rhai ```rhai
@ -204,6 +204,7 @@ for image in images {
} }
``` ```
### `bah_image_remove(image)` ### `bah_image_remove(image)`
Removes one or more images. Removes one or more images.
@ -211,7 +212,7 @@ Removes one or more images.
**Parameters:** **Parameters:**
- `image` (string): The image ID or name - `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:** **Example:**
```rhai ```rhai
@ -228,7 +229,7 @@ Pushes an image to a registry.
- `destination` (string): The destination registry/repository - `destination` (string): The destination registry/repository
- `tls_verify` (boolean): Whether to verify TLS certificates - `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:** **Example:**
```rhai ```rhai
@ -244,7 +245,7 @@ Adds an additional name to a local image.
- `image` (string): The image ID or name - `image` (string): The image ID or name
- `new_name` (string): The new name to add - `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:** **Example:**
```rhai ```rhai
@ -260,7 +261,7 @@ Pulls an image from a registry.
- `image` (string): The image to pull - `image` (string): The image to pull
- `tls_verify` (boolean): Whether to verify TLS certificates - `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:** **Example:**
```rhai ```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 - `image_name` (string): The name to give the new image
- `options` (map): A map of options created with `bah_new_commit_options()` - `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:** **Example:**
```rhai ```rhai
@ -326,7 +327,7 @@ Configures a container with options specified in a map.
- `container` (string): The container ID or name - `container` (string): The container ID or name
- `options` (map): A map of options created with `bah_new_config_options()` - `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:** **Example:**
```rhai ```rhai
@ -341,3 +342,4 @@ options.label = "version=1.0";
// Configure a container with options // Configure a container with options
let result = bah_config("my-container", options); let result = bah_config("my-container", options);
```

113
src/docs/rhai/git.md Normal file
View File

@ -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}`);
}

View File

@ -1,6 +1,6 @@
# OS Module # 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 ## 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 - `src` (string): The source file or directory path
- `dest` (string): The destination 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:** **Example:**
```rhai ```rhai
@ -49,7 +49,7 @@ Finds a file in a directory with support for wildcards.
- `dir` (string): The directory to search in - `dir` (string): The directory to search in
- `filename` (string): The filename pattern to search for (supports wildcards) - `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:** **Example:**
```rhai ```rhai
@ -68,7 +68,7 @@ Finds multiple files in a directory recursively with support for wildcards.
- `dir` (string): The directory to search in - `dir` (string): The directory to search in
- `filename` (string): The filename pattern to search for (supports wildcards) - `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:** **Example:**
```rhai ```rhai
@ -89,7 +89,7 @@ Finds a directory in a parent directory with support for wildcards.
- `dir` (string): The parent directory to search in - `dir` (string): The parent directory to search in
- `dirname` (string): The directory name pattern to search for (supports wildcards) - `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:** **Example:**
```rhai ```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 - `dir` (string): The parent directory to search in
- `dirname` (string): The directory name pattern to search for (supports wildcards) - `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:** **Example:**
```rhai ```rhai
@ -128,7 +128,7 @@ Deletes a file or directory. This function is defensive and doesn't error if the
**Parameters:** **Parameters:**
- `path` (string): The path of the file or directory to delete - `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:** **Example:**
```rhai ```rhai
@ -146,7 +146,7 @@ Creates a directory and all parent directories. This function is defensive and d
**Parameters:** **Parameters:**
- `path` (string): The path of the directory to create - `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:** **Example:**
```rhai ```rhai
@ -164,7 +164,7 @@ Gets the size of a file in bytes.
**Parameters:** **Parameters:**
- `path` (string): The path of the file - `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:** **Example:**
```rhai ```rhai
@ -182,7 +182,7 @@ Reads the contents of a file.
**Parameters:** **Parameters:**
- `path` (string): The path of the file to read - `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:** **Example:**
```rhai ```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 - `path` (string): The path of the file to write to
- `content` (string): The content to write to the file - `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:** **Example:**
```rhai ```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 - `path` (string): The path of the file to append to
- `content` (string): The content to append to the file - `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:** **Example:**
```rhai ```rhai
@ -231,7 +231,7 @@ Syncs directories using rsync (or platform equivalent).
- `src` (string): The source directory - `src` (string): The source directory
- `dest` (string): The destination 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:** **Example:**
```rhai ```rhai
@ -246,7 +246,7 @@ Changes the current working directory.
**Parameters:** **Parameters:**
- `path` (string): The path to change to - `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:** **Example:**
```rhai ```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 - `dest` (string): The destination path to save the file
- `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation) - `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:** **Example:**
```rhai ```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 - `url` (string): The URL to download from
- `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation) - `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:** **Example:**
```rhai ```rhai

View File

@ -1,6 +1,26 @@
# Process Module # 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 ## Run Functions
@ -11,7 +31,7 @@ Runs a command or multiline script with arguments.
**Parameters:** **Parameters:**
- `command` (string): The command to run - `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:** **Example:**
```rhai ```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)` ### `run_silent(command)`
@ -57,7 +55,7 @@ Runs a command or multiline script with arguments silently (without displaying o
**Parameters:** **Parameters:**
- `command` (string): The command to run - `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:** **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()` ### `new_run_options()`
Creates a new map with default 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 - `command` (string): The command to run
- `options` (map): A map of options created with `new_run_options()` - `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:** **Example:**
```rhai ```rhai
@ -123,7 +119,7 @@ Checks if a command exists in the PATH.
**Parameters:** **Parameters:**
- `cmd` (string): The command to check - `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:** **Example:**
```rhai ```rhai
@ -144,7 +140,7 @@ Kills processes matching a pattern.
**Parameters:** **Parameters:**
- `pattern` (string): The pattern to match process names against - `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:** **Example:**
```rhai ```rhai
@ -159,7 +155,7 @@ Lists processes matching a pattern (or all processes if the pattern is empty).
**Parameters:** **Parameters:**
- `pattern` (string): The pattern to match process names against (can be empty to list all processes) - `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:** **Example:**
```rhai ```rhai
@ -182,7 +178,7 @@ Gets a single process matching the pattern. Throws an error if zero or more than
**Parameters:** **Parameters:**
- `pattern` (string): The pattern to match process names against - `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:** **Example:**
```rhai ```rhai
@ -193,3 +189,4 @@ try {
} catch(err) { } catch(err) {
print(`Error: ${err}`); print(`Error: ${err}`);
} }
```

66
src/examples/git_test.rs Normal file
View File

@ -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<dyn std::error::Error>> {
// 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(())
}

View File

@ -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<dyn std::error::Error>> {
// 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(())
}

115
src/rhai/git.rs Normal file
View File

@ -0,0 +1,115 @@
//! Rhai wrappers for Git module functions
//!
//! This module provides Rhai wrappers for the functions in the Git module.
use rhai::{Engine, EvalAltResult, Array, Dynamic};
use crate::git::{self, GitError};
/// Register Git module functions with the Rhai engine
///
/// # Arguments
///
/// * `engine` - The Rhai engine to register the functions with
///
/// # Returns
///
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
pub fn register_git_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register basic git functions
engine.register_fn("git_clone", git_clone);
engine.register_fn("git_list", git_list);
engine.register_fn("git_update", git_update);
engine.register_fn("git_update_force", git_update_force);
engine.register_fn("git_update_commit", git_update_commit);
engine.register_fn("git_update_commit_push", git_update_commit_push);
engine.register_fn("git_has_changes", has_git_changes);
engine.register_fn("git_find_repos", find_matching_repos);
Ok(())
}
// Helper functions for error conversion
fn git_error_to_rhai_error<T>(result: Result<T, GitError>) -> Result<T, Box<EvalAltResult>> {
result.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Git error: {}", e).into(),
rhai::Position::NONE
))
})
}
//
// Git Function Wrappers
//
/// Wrapper for git::git_clone
///
/// Clones a git repository to a standardized location in the user's home directory.
pub fn git_clone(url: &str) -> Result<String, Box<EvalAltResult>> {
git_error_to_rhai_error(git::git_clone(url))
}
/// Wrapper for git::git_list
///
/// Lists all git repositories found in the user's ~/code directory.
pub fn git_list() -> Result<Array, Box<EvalAltResult>> {
let repos = git_error_to_rhai_error(git::git_list())?;
// Convert Vec<String> to Rhai Array
let mut array = Array::new();
for repo in repos {
array.push(Dynamic::from(repo));
}
Ok(array)
}
/// Wrapper for git::has_git_changes
///
/// Checks if a git repository has uncommitted changes.
pub fn has_git_changes(repo_path: &str) -> Result<bool, Box<EvalAltResult>> {
git_error_to_rhai_error(git::has_git_changes(repo_path))
}
/// Wrapper for git::find_matching_repos
///
/// Finds repositories matching a pattern or partial path.
pub fn find_matching_repos(pattern: &str) -> Result<Array, Box<EvalAltResult>> {
let repos = git_error_to_rhai_error(git::find_matching_repos(pattern))?;
// Convert Vec<String> to Rhai Array
let mut array = Array::new();
for repo in repos {
array.push(Dynamic::from(repo));
}
Ok(array)
}
/// Wrapper for git::git_update
///
/// Updates a git repository by pulling the latest changes.
pub fn git_update(repo_path: &str) -> Result<String, Box<EvalAltResult>> {
git_error_to_rhai_error(git::git_update(repo_path))
}
/// Wrapper for git::git_update_force
///
/// Force updates a git repository by discarding local changes and pulling the latest changes.
pub fn git_update_force(repo_path: &str) -> Result<String, Box<EvalAltResult>> {
git_error_to_rhai_error(git::git_update_force(repo_path))
}
/// Wrapper for git::git_update_commit
///
/// Commits changes in a git repository and then updates it by pulling the latest changes.
pub fn git_update_commit(repo_path: &str, message: &str) -> Result<String, Box<EvalAltResult>> {
git_error_to_rhai_error(git::git_update_commit(repo_path, message))
}
/// Wrapper for git::git_update_commit_push
///
/// Commits changes in a git repository and pushes them to the remote.
pub fn git_update_commit_push(repo_path: &str, message: &str) -> Result<String, Box<EvalAltResult>> {
git_error_to_rhai_error(git::git_update_commit_push(repo_path, message))
}

View File

@ -7,6 +7,8 @@ mod error;
mod os; mod os;
mod process; mod process;
mod buildah; mod buildah;
mod nerdctl;
mod git;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -35,7 +37,28 @@ pub use process::{
which, kill, process_list, process_get 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 // Rename copy functions to avoid conflicts
pub use os::copy as os_copy; pub use os::copy as os_copy;
@ -67,6 +90,12 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
// Register Buildah module functions // Register Buildah module functions
buildah::register_bah_module(engine)?; 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 // Future modules can be registered here
Ok(()) Ok(())

188
src/rhai/nerdctl.rs Normal file
View File

@ -0,0 +1,188 @@
//! Rhai wrappers for Nerdctl module functions
//!
//! This module provides Rhai wrappers for the functions in the Nerdctl module.
use rhai::{Engine, EvalAltResult, Array, Dynamic, Map};
use crate::virt::nerdctl::{self, NerdctlError, Image};
use crate::process::CommandResult;
/// Register Nerdctl module functions with the Rhai engine
///
/// # Arguments
///
/// * `engine` - The Rhai engine to register the functions with
///
/// # Returns
///
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
pub fn register_nerdctl_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register types
register_nerdctl_types(engine)?;
// Register container functions
engine.register_fn("nerdctl_run", nerdctl_run);
engine.register_fn("nerdctl_run_with_name", nerdctl_run_with_name);
engine.register_fn("nerdctl_run_with_port", nerdctl_run_with_port);
engine.register_fn("new_run_options", new_run_options);
engine.register_fn("nerdctl_exec", nerdctl_exec);
engine.register_fn("nerdctl_copy", nerdctl_copy);
engine.register_fn("nerdctl_stop", nerdctl_stop);
engine.register_fn("nerdctl_remove", nerdctl_remove);
engine.register_fn("nerdctl_list", nerdctl_list);
// Register image functions
engine.register_fn("nerdctl_images", nerdctl_images);
engine.register_fn("nerdctl_image_remove", nerdctl_image_remove);
engine.register_fn("nerdctl_image_push", nerdctl_image_push);
engine.register_fn("nerdctl_image_tag", nerdctl_image_tag);
engine.register_fn("nerdctl_image_pull", nerdctl_image_pull);
engine.register_fn("nerdctl_image_commit", nerdctl_image_commit);
engine.register_fn("nerdctl_image_build", nerdctl_image_build);
Ok(())
}
/// Register Nerdctl module types with the Rhai engine
fn register_nerdctl_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register Image type and methods
engine.register_type_with_name::<Image>("NerdctlImage");
// Register getters for Image properties
engine.register_get("id", |img: &mut Image| img.id.clone());
engine.register_get("repository", |img: &mut Image| img.repository.clone());
engine.register_get("tag", |img: &mut Image| img.tag.clone());
engine.register_get("size", |img: &mut Image| img.size.clone());
engine.register_get("created", |img: &mut Image| img.created.clone());
Ok(())
}
// Helper functions for error conversion
fn nerdctl_error_to_rhai_error<T>(result: Result<T, NerdctlError>) -> Result<T, Box<EvalAltResult>> {
result.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Nerdctl error: {}", e).into(),
rhai::Position::NONE
))
})
}
/// Create a new Map with default run options
pub fn new_run_options() -> Map {
let mut map = Map::new();
map.insert("name".into(), Dynamic::UNIT);
map.insert("detach".into(), Dynamic::from(true));
map.insert("ports".into(), Dynamic::from(Array::new()));
map.insert("snapshotter".into(), Dynamic::from("native"));
map
}
//
// Container Function Wrappers
//
/// Wrapper for nerdctl::run
///
/// Run a container from an image.
pub fn nerdctl_run(image: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::run(image, None, true, None, None))
}
/// Run a container with a name
pub fn nerdctl_run_with_name(image: &str, name: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::run(image, Some(name), true, None, None))
}
/// Run a container with a port mapping
pub fn nerdctl_run_with_port(image: &str, name: &str, port: &str) -> Result<CommandResult, Box<EvalAltResult>> {
let ports = vec![port];
nerdctl_error_to_rhai_error(nerdctl::run(image, Some(name), true, Some(&ports), None))
}
/// Wrapper for nerdctl::exec
///
/// Execute a command in a container.
pub fn nerdctl_exec(container: &str, command: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::exec(container, command))
}
/// Wrapper for nerdctl::copy
///
/// Copy files between container and local filesystem.
pub fn nerdctl_copy(source: &str, dest: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::copy(source, dest))
}
/// Wrapper for nerdctl::stop
///
/// Stop a container.
pub fn nerdctl_stop(container: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::stop(container))
}
/// Wrapper for nerdctl::remove
///
/// Remove a container.
pub fn nerdctl_remove(container: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::remove(container))
}
/// Wrapper for nerdctl::list
///
/// List containers.
pub fn nerdctl_list(all: bool) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::list(all))
}
//
// Image Function Wrappers
//
/// Wrapper for nerdctl::images
///
/// List images in local storage.
pub fn nerdctl_images() -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::images())
}
/// Wrapper for nerdctl::image_remove
///
/// Remove one or more images.
pub fn nerdctl_image_remove(image: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_remove(image))
}
/// Wrapper for nerdctl::image_push
///
/// Push an image to a registry.
pub fn nerdctl_image_push(image: &str, destination: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_push(image, destination))
}
/// Wrapper for nerdctl::image_tag
///
/// Add an additional name to a local image.
pub fn nerdctl_image_tag(image: &str, new_name: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_tag(image, new_name))
}
/// Wrapper for nerdctl::image_pull
///
/// Pull an image from a registry.
pub fn nerdctl_image_pull(image: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_pull(image))
}
/// Wrapper for nerdctl::image_commit
///
/// Commit a container to an image.
pub fn nerdctl_image_commit(container: &str, image_name: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_commit(container, image_name))
}
/// Wrapper for nerdctl::image_build
///
/// Build an image using a Dockerfile.
pub fn nerdctl_image_build(tag: &str, context_path: &str) -> Result<CommandResult, Box<EvalAltResult>> {
nerdctl_error_to_rhai_error(nerdctl::image_build(tag, context_path))
}

View File

@ -205,4 +205,50 @@ mod tests {
let result = engine.eval::<bool>(script).unwrap(); let result = engine.eval::<bool>(script).unwrap();
assert!(result); assert!(result);
} }
// Git Module Tests
#[test]
fn test_git_module_registration() {
let mut engine = Engine::new();
register(&mut engine).unwrap();
// Test that git functions are registered
let script = r#"
// Check if git_clone function exists
let fn_exists = is_def_fn("git_clone");
fn_exists
"#;
let result = engine.eval::<bool>(script).unwrap();
assert!(result);
}
#[test]
fn test_git_parse_url() {
let mut engine = Engine::new();
register(&mut engine).unwrap();
// Test parsing a git URL
let script = r#"
// We can't directly test git_clone without actually cloning,
// but we can test that the function exists and doesn't error
// when called with invalid parameters
let result = false;
try {
// This should fail but not crash
git_clone("invalid-url");
} catch(err) {
// Expected error
result = err.contains("Git error");
}
result
"#;
let result = engine.eval::<bool>(script).unwrap();
assert!(result);
}
} }

View File

@ -0,0 +1,131 @@
// Test script for Git module functions
// Import required modules
import "os" as os;
import "process" as process;
// Test git_clone function
fn test_git_clone() {
// Use a public repository for testing
let repo_url = "https://github.com/rhaiscript/rhai.git";
// Clone the repository
print("Testing git_clone...");
let result = git_clone(repo_url);
// Print the result
print(`Clone result: ${result}`);
// Verify the repository exists
if result.contains("already exists") {
print("Repository already exists, test passed");
return true;
}
// Check if the path exists
if exist(result) {
print("Repository cloned successfully, test passed");
return true;
}
print("Repository clone failed");
return false;
}
// Test git_list function
fn test_git_list() {
print("Testing git_list...");
let repos = git_list();
print(`Found ${repos.len()} repositories`);
// Print the first few repositories
let count = if repos.len() > 3 { 3 } else { repos.len() };
for i in range(0, count) {
print(` - ${repos[i]}`);
}
return repos.len() > 0;
}
// Test git_has_changes function
fn test_git_has_changes() {
print("Testing git_has_changes...");
// Get a repository from the list
let repos = git_list();
if repos.len() == 0 {
print("No repositories found, skipping test");
return true;
}
let repo = repos[0];
let has_changes = git_has_changes(repo);
print(`Repository ${repo} has changes: ${has_changes}`);
return true;
}
// Test find_matching_repos function
fn test_find_matching_repos() {
print("Testing find_matching_repos...");
// Get all repositories with wildcard
let all_repos = git_list();
if all_repos.len() == 0 {
print("No repositories found, skipping test");
return true;
}
// Extract a part of the first repo name to search for
let repo_name = all_repos[0].split("/").last();
let search_pattern = repo_name.substring(0, 3) + "*";
print(`Searching for repositories matching pattern: ${search_pattern}`);
let matching = find_matching_repos(search_pattern);
print(`Found ${matching.len()} matching repositories`);
for repo in matching {
print(` - ${repo}`);
}
return matching.len() > 0;
}
// Run the tests
fn run_tests() {
let tests = [
#{ name: "git_clone", fn: test_git_clone },
#{ name: "git_list", fn: test_git_list },
#{ name: "git_has_changes", fn: test_git_has_changes },
#{ name: "find_matching_repos", fn: test_find_matching_repos }
];
let passed = 0;
let failed = 0;
for test in tests {
print(`\nRunning test: ${test.name}`);
let result = false;
try {
result = test.fn();
} catch(err) {
print(`Test ${test.name} threw an error: ${err}`);
result = false;
}
if result {
print(`Test ${test.name} PASSED`);
passed += 1;
} else {
print(`Test ${test.name} FAILED`);
failed += 1;
}
}
print(`\nTest summary: ${passed} passed, ${failed} failed`);
}
// Run all tests
run_tests();

27
src/run_git_test.rs Normal file
View File

@ -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<dyn Error>> {
// 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(())
}

42
src/test_git.rhai Normal file
View File

@ -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 ===");