...
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run

This commit is contained in:
2025-05-12 06:09:25 +03:00
parent 8285fdb7b9
commit 516d0177e7
73 changed files with 38 additions and 37 deletions

7
docs/docs/intro.md Normal file
View File

@@ -0,0 +1,7 @@
---
title: "intro"
sidebar_position: 1
---
# HeroScript

View File

@@ -0,0 +1,105 @@
# Buildah Module Tests
This document describes the test scripts for the Buildah module in the SAL library. These tests verify the functionality of the Buildah module's container and image operations.
## Test Structure
The tests are organized into three main scripts:
1. **Builder Pattern** (`01_builder_pattern.rhai`): Tests for the Builder pattern, including creating containers, running commands, and working with container content.
2. **Image Operations** (`02_image_operations.rhai`): Tests for image-related operations like pulling, tagging, listing, and removing images.
3. **Container Operations** (`03_container_operations.rhai`): Tests for container-related operations like configuration, isolation, and content management.
Additionally, there's a runner script (`run_all_tests.rhai`) that executes all tests and reports results. The runner script contains simplified versions of the individual tests to avoid dependency issues.
## Running the Tests
To run all tests, execute the following command from the project root:
```bash
herodo --path src/rhai_tests/buildah/run_all_tests.rhai
```
To run individual test scripts:
```bash
herodo --path src/rhai_tests/buildah/01_builder_pattern.rhai
```
## Test Details
### Builder Pattern Test
The Builder Pattern test (`01_builder_pattern.rhai`) verifies the following functions:
- `bah_new`: Creating a new Builder with a container from a specified image
- Builder properties: `container_id`, `name`, `image`, `debug_mode`
- `run`: Running commands in the container
- `write_content`: Writing content to files in the container
- `read_content`: Reading content from files in the container
- `set_entrypoint`: Setting the container's entrypoint
- `set_cmd`: Setting the container's command
- `add`: Adding files to the container
- `copy`: Copying files to the container
- `commit`: Committing the container to an image
- `remove`: Removing the container
- `images`: Listing images
- `image_remove`: Removing images
### Image Operations Test
The Image Operations test (`02_image_operations.rhai`) verifies the following functions:
- `image_pull`: Pulling images from registries
- `image_tag`: Tagging images
- `images`: Listing images
- `build`: Building images from Dockerfiles
- `image_remove`: Removing images
The test creates a temporary directory with a Dockerfile for testing the build functionality.
### Container Operations Test
The Container Operations test (`03_container_operations.rhai`) verifies the following functions:
- `reset`: Resetting a Builder by removing its container
- `config`: Configuring container properties
- `run_with_isolation`: Running commands with isolation
- Content operations: Creating and executing scripts in the container
- `commit` with options: Committing a container with additional configuration
## Test Runner
The test runner script (`run_all_tests.rhai`) provides a framework for executing all tests and reporting results. It:
1. Checks if Buildah is available before running tests
2. Skips tests if Buildah is not available
3. Contains simplified versions of each test
4. Runs each test in a try/catch block to handle errors
5. Catches and reports any errors
6. Provides a summary of passed, failed, and skipped tests
## Buildah Requirements
These tests require the Buildah tool to be installed and available in the system's PATH. The tests will check for Buildah's availability and skip the tests if it's not found, rather than failing.
## Adding New Tests
To add a new test:
1. Create a new Rhai script in the `src/rhai_tests/buildah` directory
2. Add a new test section to the `run_all_tests.rhai` script
3. Update this documentation to include information about the new test
## Best Practices for Writing Tests
When writing tests for the Buildah module:
1. Always check if Buildah is available before running tests
2. Use unique names for containers and images to avoid conflicts
3. Clean up any containers, images, or files created during testing
4. Use assertions to verify expected behavior
5. Print clear messages about what's being tested
6. Handle errors gracefully
7. Make tests independent of each other
8. Keep tests focused on specific functionality

View File

@@ -0,0 +1,71 @@
# Continuous Integration for Rhai Tests
This document describes the continuous integration (CI) workflow for running Rhai tests in the SAL library.
## GitHub Actions Workflow
The SAL project includes a GitHub Actions workflow that automatically runs all Rhai tests whenever changes are made to relevant files. This ensures that the Rhai integration continues to work correctly as the codebase evolves.
### Workflow File
The workflow is defined in `.github/workflows/rhai-tests.yml`.
### Trigger Events
The workflow runs automatically when:
1. Changes are pushed to the `main` or `master` branch that affect:
- Rhai test scripts (`src/rhai_tests/**`)
- Rhai module code (`src/rhai/**`)
- Git module code (`src/git/**`)
- OS module code (`src/os/**`)
- The test runner script (`run_rhai_tests.sh`)
- The workflow file itself (`.github/workflows/rhai-tests.yml`)
2. A pull request is opened or updated that affects the same files.
3. The workflow is manually triggered using the GitHub Actions interface.
### Workflow Steps
The workflow performs the following steps:
1. **Checkout Code**: Checks out the repository code.
2. **Set up Rust**: Installs the Rust toolchain.
3. **Cache Dependencies**: Caches Rust dependencies to speed up builds.
4. **Build herodo**: Builds the `herodo` binary used to run Rhai scripts.
5. **Install Dependencies**: Installs system dependencies like Git and curl.
6. **Run Rhai Tests**: Runs the `run_rhai_tests.sh` script to execute all Rhai tests.
7. **Check for Failures**: Verifies that all tests passed.
### Test Results
The workflow will fail if any Rhai test fails. This prevents changes that break the Rhai integration from being merged.
## Local Testing
Before pushing changes, you can run the same tests locally using the `run_rhai_tests.sh` script:
```bash
./run_rhai_tests.sh
```
This will produce the same test results as the CI workflow, allowing you to catch and fix issues before pushing your changes.
## Logs
The test runner script creates a log file (`run_rhai_tests.log`) that contains the output of all tests. This log is used by the CI workflow to check for test failures.
## Adding New Tests
When adding new tests, make sure they are included in the appropriate module's test runner script (`run_all_tests.rhai`). The CI workflow will automatically run the new tests.
## Troubleshooting
If the CI workflow fails, check the GitHub Actions logs for details. Common issues include:
1. **Missing Dependencies**: Ensure all required dependencies are installed.
2. **Test Failures**: Fix any failing tests.
3. **Build Errors**: Fix any errors in the Rust code.
If you need to modify the workflow, edit the `.github/workflows/rhai-tests.yml` file.

View File

@@ -0,0 +1,81 @@
# Git Module Tests
This document describes the test scripts for the Git module in the SAL library. These tests verify the functionality of the Git module's repository management and Git operations.
## Test Structure
The tests are organized into two main scripts:
1. **Basic Git Operations** (`01_git_basic.rhai`): Tests basic Git functionality like creating a GitTree, listing repositories, finding repositories, and cloning repositories.
2. **Git Repository Operations** (`02_git_operations.rhai`): Tests Git operations like pull, reset, commit, and push.
Additionally, there's a runner script (`run_all_tests.rhai`) that executes all tests and reports results. The runner script contains simplified versions of the individual tests to avoid dependency issues.
## Running the Tests
To run all tests, execute the following command from the project root:
```bash
herodo --path src/rhai_tests/git/run_all_tests.rhai
```
To run individual test scripts:
```bash
herodo --path src/rhai_tests/git/01_git_basic.rhai
```
## Test Details
### Basic Git Operations Test
The basic Git operations test (`01_git_basic.rhai`) verifies the following functions:
- `git_tree_new`: Creating a GitTree
- `list`: Listing repositories in a GitTree
- `find`: Finding repositories matching a pattern
- `get`: Getting or cloning a repository
- `path`: Getting the path of a repository
- `has_changes`: Checking if a repository has changes
The test creates a temporary directory, performs operations on it, and then cleans up after itself.
### Git Repository Operations Test
The Git repository operations test (`02_git_operations.rhai`) verifies the following functions:
- `pull`: Pulling changes from a remote repository
- `reset`: Resetting local changes
- `commit`: Committing changes (method existence only)
- `push`: Pushing changes to a remote repository (method existence only)
Note: The test does not actually commit or push changes to avoid modifying remote repositories. It only verifies that the methods exist and can be called.
## Test Runner
The test runner script (`run_all_tests.rhai`) provides a framework for executing all tests and reporting results. It:
1. Contains simplified versions of each test
2. Runs each test in a try/catch block to handle errors
3. Catches and reports any errors
4. Provides a summary of passed and failed tests
## Adding New Tests
To add a new test:
1. Create a new Rhai script in the `src/rhai_tests/git` directory
2. Add a new test section to the `run_all_tests.rhai` script
3. Update this documentation to include information about the new test
## Best Practices for Writing Tests
When writing tests for the Git module:
1. Always clean up temporary files and directories
2. Use assertions to verify expected behavior
3. Print clear messages about what's being tested
4. Handle errors gracefully
5. Make tests independent of each other
6. Avoid tests that modify remote repositories
7. Keep tests focused on specific functionality

85
docs/docs/rhai/index.md Normal file
View File

@@ -0,0 +1,85 @@
# Rhai Scripting in SAL
This documentation covers the Rhai scripting integration in the SAL (System Abstraction Layer) library.
## Overview
SAL provides integration with the [Rhai scripting language](https://rhai.rs/), allowing you to use SAL's functionality in scripts. This enables automation of system tasks, testing, and more complex operations without having to write Rust code.
## Modules
SAL exposes the following modules to Rhai scripts:
- [OS Module](os_module_tests.md): File system operations, downloads, and package management
- Process Module: Process management and command execution
- Git Module: Git repository operations
- Text Module: Text processing utilities
- Buildah Module: Container image building
- Nerdctl Module: Container runtime operations
- RFS Module: Remote file system operations
- Redis Client Module: Redis database connection and operations
- PostgreSQL Client Module: PostgreSQL database connection and operations
## Running Rhai Scripts
You can run Rhai scripts using the `herodo` binary:
```bash
herodo --path path/to/script.rhai
```
## Testing
SAL includes test scripts for verifying the functionality of its Rhai integration. These tests are located in the `src/rhai_tests` directory and are organized by module.
- [OS Module Tests](os_module_tests.md): Tests for file system, download, and package management operations
- [Git Module Tests](git_module_tests.md): Tests for Git repository management and operations
- [Process Module Tests](process_module_tests.md): Tests for command execution and process management
- [Redis Client Module Tests](redisclient_module_tests.md): Tests for Redis connection and operations
- [PostgreSQL Client Module Tests](postgresclient_module_tests.md): Tests for PostgreSQL connection and operations
- [Text Module Tests](text_module_tests.md): Tests for text manipulation, normalization, replacement, and template rendering
- [Buildah Module Tests](buildah_module_tests.md): Tests for container and image operations
- [Nerdctl Module Tests](nerdctl_module_tests.md): Tests for container and image operations using nerdctl
- [RFS Module Tests](rfs_module_tests.md): Tests for remote filesystem operations and filesystem layers
- [Running Tests](running_tests.md): Instructions for running all Rhai tests
- [CI Workflow](ci_workflow.md): Continuous integration workflow for Rhai tests
## Examples
For examples of how to use SAL's Rhai integration, see the `examples` directory in the project root. These examples demonstrate various features and use cases.
## Writing Your Own Scripts
When writing Rhai scripts that use SAL:
1. Import the necessary modules (they're automatically registered)
2. Use the functions provided by each module
3. Handle errors appropriately
4. Clean up resources when done
Example:
```rhai
// Simple example of using the OS module
let test_dir = "my_test_dir";
mkdir(test_dir);
if exist(test_dir) {
print(`Directory ${test_dir} created successfully`);
// Create a file
let test_file = test_dir + "/test.txt";
file_write(test_file, "Hello, world!");
// Read the file
let content = file_read(test_file);
print(`File content: ${content}`);
// Clean up
delete(test_dir);
}
```
## API Reference
For detailed information about the functions available in each module, refer to the module-specific documentation.

View File

@@ -0,0 +1,116 @@
# Nerdctl Module Tests
This document describes the test scripts for the Nerdctl module in the SAL library. These tests verify the functionality of the Nerdctl module's container and image operations.
## Test Structure
The tests are organized into three main scripts:
1. **Container Operations** (`01_container_operations.rhai`): Tests for basic container operations like creating, running, executing commands, and removing containers.
2. **Image Operations** (`02_image_operations.rhai`): Tests for image-related operations like pulling, tagging, listing, building, and removing images.
3. **Container Builder Pattern** (`03_container_builder.rhai`): Tests for the Container Builder pattern, which provides a fluent interface for configuring and running containers.
Additionally, there's a runner script (`run_all_tests.rhai`) that executes all tests and reports results. The runner script contains simplified versions of the individual tests to avoid dependency issues.
## Running the Tests
To run all tests, execute the following command from the project root:
```bash
herodo --path src/rhai_tests/nerdctl/run_all_tests.rhai
```
To run individual test scripts:
```bash
herodo --path src/rhai_tests/nerdctl/01_container_operations.rhai
```
## Test Details
### Container Operations Test
The Container Operations test (`01_container_operations.rhai`) verifies the following functions:
- `nerdctl_container_new`: Creating a new Container
- Container properties: `name`, `container_id`, `image`, `detach`
- `with_image`: Setting the container image
- `with_detach`: Setting detach mode
- `with_env` and `with_envs`: Setting environment variables
- `with_port` and `with_ports`: Setting port mappings
- `with_volume`: Setting volume mounts
- `with_cpu_limit` and `with_memory_limit`: Setting resource limits
- `run`: Running the container
- `exec`: Executing commands in the container
- `logs`: Getting container logs
- `stop`: Stopping the container
- `remove`: Removing the container
### Image Operations Test
The Image Operations test (`02_image_operations.rhai`) verifies the following functions:
- `nerdctl_image_pull`: Pulling images from registries
- `nerdctl_images`: Listing images
- `nerdctl_image_tag`: Tagging images
- `nerdctl_image_build`: Building images from Dockerfiles
- `nerdctl_run_with_name`: Running containers from images
- `nerdctl_stop` and `nerdctl_remove`: Stopping and removing containers
- `nerdctl_image_remove`: Removing images
The test creates a temporary directory with a Dockerfile for testing the build functionality.
### Container Builder Pattern Test
The Container Builder Pattern test (`03_container_builder.rhai`) verifies the following functions:
- `nerdctl_container_from_image`: Creating a container from an image
- `reset`: Resetting container configuration
- `with_detach`: Setting detach mode
- `with_ports`: Setting multiple port mappings
- `with_volumes`: Setting multiple volume mounts
- `with_envs`: Setting multiple environment variables
- `with_network`: Setting network
- `with_cpu_limit` and `with_memory_limit`: Setting resource limits
- `run`: Running the container
- `exec`: Executing commands in the container
- `stop`: Stopping the container
- `remove`: Removing the container
The test also verifies that environment variables and volume mounts work correctly by writing and reading files between the container and the host.
## Test Runner
The test runner script (`run_all_tests.rhai`) provides a framework for executing all tests and reporting results. It:
1. Checks if nerdctl is available before running tests
2. Skips tests if nerdctl is not available
3. Contains simplified versions of each test
4. Runs each test in a try/catch block to handle errors
5. Catches and reports any errors
6. Provides a summary of passed, failed, and skipped tests
## Nerdctl Requirements
These tests require the nerdctl tool to be installed and available in the system's PATH. The tests will check for nerdctl's availability and skip the tests if it's not found, rather than failing.
## Adding New Tests
To add a new test:
1. Create a new Rhai script in the `src/rhai_tests/nerdctl` directory
2. Add a new test section to the `run_all_tests.rhai` script
3. Update this documentation to include information about the new test
## Best Practices for Writing Tests
When writing tests for the Nerdctl module:
1. Always check if nerdctl is available before running tests
2. Use unique names for containers and images to avoid conflicts
3. Clean up any containers, images, or files created during testing
4. Use assertions to verify expected behavior
5. Print clear messages about what's being tested
6. Handle errors gracefully
7. Make tests independent of each other
8. Keep tests focused on specific functionality

View File

@@ -0,0 +1,105 @@
# OS Module Tests
This document describes the test scripts for the OS module in the SAL library. These tests verify the functionality of the OS module's file system operations, download capabilities, and package management features.
## Test Structure
The tests are organized into three main scripts:
1. **File Operations** (`01_file_operations.rhai`): Tests file system operations like creating, reading, writing, and manipulating files and directories.
2. **Download Operations** (`02_download_operations.rhai`): Tests downloading files from the internet and related operations.
3. **Package Operations** (`03_package_operations.rhai`): Tests package management functionality.
Additionally, there's a runner script (`run_all_tests.rhai`) that executes all tests and reports results. The runner script contains simplified versions of the individual tests to avoid dependency on the `run_script` function.
## Running the Tests
To run all tests, execute the following command from the project root:
```bash
# Assume that you have the herodo binary/built into your system
herodo --path src/rhai_tests/os/run_all_tests.rhai
```
To run individual test scripts:
```bash
# Assume that you have the herodo binary/built into your system
herodo --path src/rhai_tests/os/01_file_operations.rhai
```
## Test Details
### File Operations Test
The file operations test (`01_file_operations.rhai`) verifies the following functions:
- `mkdir`: Creating directories
- `file_write`: Writing content to files
- `file_read`: Reading content from files
- `file_size`: Getting file size
- `file_write_append`: Appending content to files
- `copy`: Copying files
- `mv`: Moving files
- `find_file`: Finding a single file matching a pattern
- `find_files`: Finding multiple files matching a pattern
- `find_dir`: Finding a single directory matching a pattern
- `find_dirs`: Finding multiple directories matching a pattern
- `chdir`: Changing the current working directory
- `rsync`: Synchronizing directories
- `delete`: Deleting files and directories
- `exist`: Checking if files or directories exist
The test creates a temporary directory structure, performs operations on it, and then cleans up after itself.
### Download Operations Test
The download operations test (`02_download_operations.rhai`) verifies the following functions:
- `which`: Checking if a command exists in the system PATH
- `cmd_ensure_exists`: Ensuring commands exist
- `download_file`: Downloading a file from a URL
- `chmod_exec`: Making a file executable
The test downloads a small file from GitHub, verifies its content, and then cleans up.
### Package Operations Test
The package operations test (`03_package_operations.rhai`) verifies the following functions:
- `package_platform`: Getting the current platform
- `package_set_debug`: Setting debug mode for package operations
- `package_is_installed`: Checking if a package is installed
- `package_search`: Searching for packages
- `package_list`: Listing installed packages
Note: The test does not verify `package_install`, `package_remove`, `package_update`, or `package_upgrade` as these require root privileges and could modify the system state.
## Test Runner
The test runner script (`run_all_tests.rhai`) provides a framework for executing all tests and reporting results. It:
1. Contains simplified versions of each test
2. Runs each test in a try/catch block to handle errors
3. Catches and reports any errors
4. Provides a summary of passed and failed tests
## Adding New Tests
To add a new test:
1. Create a new Rhai script in the `src/rhai_tests/os` directory
2. Add a new test section to the `run_all_tests.rhai` script
3. Update this documentation to include information about the new test
## Best Practices for Writing Tests
When writing tests for the OS module:
1. Always clean up temporary files and directories
2. Use assertions to verify expected behavior
3. Print clear messages about what's being tested
4. Handle errors gracefully
5. Make tests independent of each other
6. Avoid tests that require root privileges when possible
7. Keep tests focused on specific functionality

View File

@@ -0,0 +1,188 @@
# PostgreSQL Client Module Tests
The PostgreSQL client module provides functions for connecting to and interacting with PostgreSQL databases. These tests verify the functionality of the module.
## PostgreSQL Client Features
The PostgreSQL client module provides the following features:
1. **Basic PostgreSQL Operations**: Execute queries, fetch results, etc.
2. **Connection Management**: Automatic connection handling and reconnection
3. **Builder Pattern for Configuration**: Flexible configuration with authentication support
4. **PostgreSQL Installer**: Install and configure PostgreSQL using nerdctl
5. **Database Management**: Create databases and execute SQL scripts
## Prerequisites
For basic PostgreSQL operations:
- PostgreSQL server must be running and accessible
- Environment variables should be set for connection details:
- `POSTGRES_HOST`: PostgreSQL server host (default: localhost)
- `POSTGRES_PORT`: PostgreSQL server port (default: 5432)
- `POSTGRES_USER`: PostgreSQL username (default: postgres)
- `POSTGRES_PASSWORD`: PostgreSQL password
- `POSTGRES_DB`: PostgreSQL database name (default: postgres)
For PostgreSQL installer:
- nerdctl must be installed and working
- Docker images must be accessible
- Sufficient permissions to create and manage containers
## Test Files
### 01_postgres_connection.rhai
Tests basic PostgreSQL connection and operations:
- Connecting to PostgreSQL
- Pinging the server
- Creating a table
- Inserting data
- Querying data
- Dropping a table
- Resetting the connection
### 02_postgres_installer.rhai
Tests PostgreSQL installer functionality:
- Installing PostgreSQL using nerdctl
- Creating a database
- Executing SQL scripts
- Checking if PostgreSQL is running
### run_all_tests.rhai
Runs all PostgreSQL client module tests and provides a summary of the results.
## Running the Tests
You can run the tests using the `herodo` command:
```bash
herodo --path src/rhai_tests/postgresclient/run_all_tests.rhai
```
Or run individual tests:
```bash
herodo --path src/rhai_tests/postgresclient/01_postgres_connection.rhai
```
## Available Functions
### Connection Functions
- `pg_connect()`: Connect to PostgreSQL using environment variables
- `pg_ping()`: Ping the PostgreSQL server to check if it's available
- `pg_reset()`: Reset the PostgreSQL client connection
### Query Functions
- `pg_execute(query)`: Execute a query and return the number of affected rows
- `pg_query(query)`: Execute a query and return the results as an array of maps
- `pg_query_one(query)`: Execute a query and return a single row as a map
### Installer Functions
- `pg_install(container_name, version, port, username, password)`: Install PostgreSQL using nerdctl
- `pg_create_database(container_name, db_name)`: Create a new database in PostgreSQL
- `pg_execute_sql(container_name, db_name, sql)`: Execute a SQL script in PostgreSQL
- `pg_is_running(container_name)`: Check if PostgreSQL is running
## Authentication Support
The PostgreSQL client module will support authentication using the builder pattern in a future update.
The backend implementation is ready, but the Rhai bindings are still in development.
When implemented, the builder pattern will support the following configuration options:
- Host: Set the PostgreSQL host
- Port: Set the PostgreSQL port
- User: Set the PostgreSQL username
- Password: Set the PostgreSQL password
- Database: Set the PostgreSQL database name
- Application name: Set the application name
- Connection timeout: Set the connection timeout in seconds
- SSL mode: Set the SSL mode
## Example Usage
### Basic PostgreSQL Operations
```rust
// Connect to PostgreSQL
if (pg_connect()) {
print("Connected to PostgreSQL!");
// Create a table
let create_table_query = "CREATE TABLE IF NOT EXISTS test_table (id SERIAL PRIMARY KEY, name TEXT)";
pg_execute(create_table_query);
// Insert data
let insert_query = "INSERT INTO test_table (name) VALUES ('test')";
pg_execute(insert_query);
// Query data
let select_query = "SELECT * FROM test_table";
let results = pg_query(select_query);
// Process results
for (result in results) {
print(`ID: ${result.id}, Name: ${result.name}`);
}
// Clean up
let drop_query = "DROP TABLE test_table";
pg_execute(drop_query);
}
```
### PostgreSQL Installer
```rust
// Install PostgreSQL
let container_name = "my-postgres";
let postgres_version = "15";
let postgres_port = 5432;
let postgres_user = "myuser";
let postgres_password = "mypassword";
if (pg_install(container_name, postgres_version, postgres_port, postgres_user, postgres_password)) {
print("PostgreSQL installed successfully!");
// Create a database
let db_name = "mydb";
if (pg_create_database(container_name, db_name)) {
print(`Database '${db_name}' created successfully!`);
// Execute a SQL script
let create_table_sql = `
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
`;
let result = pg_execute_sql(container_name, db_name, create_table_sql);
print("Table created successfully!");
// Insert data
let insert_sql = "#
INSERT INTO users (name, email) VALUES
('John Doe', 'john@example.com'),
('Jane Smith', 'jane@example.com');
#";
result = pg_execute_sql(container_name, db_name, insert_sql);
print("Data inserted successfully!");
// Query data
let query_sql = "SELECT * FROM users;";
result = pg_execute_sql(container_name, db_name, query_sql);
print(`Query result: ${result}`);
}
}
```

View File

@@ -0,0 +1,79 @@
# Process Module Tests
This document describes the test scripts for the Process module in the SAL library. These tests verify the functionality of the Process module's command execution and process management features.
## Test Structure
The tests are organized into two main scripts:
1. **Command Execution** (`01_command_execution.rhai`): Tests command execution functions like `run()` and `which()`.
2. **Process Management** (`02_process_management.rhai`): Tests process management functions like `process_list()` and `process_get()`.
Additionally, there's a runner script (`run_all_tests.rhai`) that executes all tests and reports results. The runner script contains simplified versions of the individual tests to avoid dependency issues.
## Running the Tests
To run all tests, execute the following command from the project root:
```bash
herodo --path src/rhai_tests/process/run_all_tests.rhai
```
To run individual test scripts:
```bash
herodo --path src/rhai_tests/process/01_command_execution.rhai
```
## Test Details
### Command Execution Test
The command execution test (`01_command_execution.rhai`) verifies the following functions:
- `run()`: Running shell commands
- `run().do()`: Executing commands and capturing output
- `run().silent()`: Running commands without displaying output
- `run().ignore_error()`: Running commands that might fail without throwing errors
- `which()`: Finding the path of an executable
The test runs various commands and verifies their output and exit status.
### Process Management Test
The process management test (`02_process_management.rhai`) verifies the following functions:
- `process_list()`: Listing running processes
- `process_get()`: Getting information about a specific process
- Process properties: Accessing process information like PID, name, CPU usage, and memory usage
The test lists running processes and verifies that their properties are accessible.
## Test Runner
The test runner script (`run_all_tests.rhai`) provides a framework for executing all tests and reporting results. It:
1. Contains simplified versions of each test
2. Runs each test in a try/catch block to handle errors
3. Catches and reports any errors
4. Provides a summary of passed and failed tests
## Adding New Tests
To add a new test:
1. Create a new Rhai script in the `src/rhai_tests/process` directory
2. Add a new test section to the `run_all_tests.rhai` script
3. Update this documentation to include information about the new test
## Best Practices for Writing Tests
When writing tests for the Process module:
1. Use assertions to verify expected behavior
2. Print clear messages about what's being tested
3. Handle errors gracefully
4. Make tests independent of each other
5. Avoid tests that could disrupt the system (e.g., killing important processes)
6. Keep tests focused on specific functionality
7. Clean up any resources created during testing

View File

@@ -0,0 +1,125 @@
# Redis Client Module Tests
This document describes the test scripts for the Redis client module in the SAL library. These tests verify the functionality of the Redis client module's connection management and Redis operations.
## Redis Client Features
The Redis client module provides the following features:
1. **Basic Redis Operations**: SET, GET, DEL, etc.
2. **Hash Operations**: HSET, HGET, HGETALL, HDEL
3. **List Operations**: RPUSH, LPUSH, LLEN, LRANGE
4. **Connection Management**: Automatic connection handling and reconnection
5. **Builder Pattern for Configuration**: Flexible configuration with authentication support
## Test Structure
The tests are organized into two main scripts:
1. **Redis Connection** (`01_redis_connection.rhai`): Tests basic Redis connection and simple operations like PING, SET, GET, and DEL.
2. **Redis Operations** (`02_redis_operations.rhai`): Tests more advanced Redis operations like hash operations (HSET, HGET, HGETALL, HDEL) and list operations (RPUSH, LLEN, LRANGE).
Additionally, there's a runner script (`run_all_tests.rhai`) that executes all tests and reports results. The runner script contains simplified versions of the individual tests to avoid dependency issues.
## Running the Tests
To run all tests, execute the following command from the project root:
```bash
herodo --path src/rhai_tests/redisclient/run_all_tests.rhai
```
To run individual test scripts:
```bash
herodo --path src/rhai_tests/redisclient/01_redis_connection.rhai
```
## Test Details
### Redis Connection Test
The Redis connection test (`01_redis_connection.rhai`) verifies the following functions:
- `redis_ping`: Checking if the Redis server is available
- `redis_set`: Setting a key-value pair
- `redis_get`: Getting a value by key
- `redis_del`: Deleting a key
The test creates a temporary key, performs operations on it, and then cleans up after itself.
### Redis Operations Test
The Redis operations test (`02_redis_operations.rhai`) verifies the following functions:
- Hash operations:
- `redis_hset`: Setting a field in a hash
- `redis_hget`: Getting a field from a hash
- `redis_hgetall`: Getting all fields and values from a hash
- `redis_hdel`: Deleting a field from a hash
- List operations:
- `redis_rpush`: Adding elements to a list
- `redis_llen`: Getting the length of a list
- `redis_lrange`: Getting a range of elements from a list
The test creates temporary keys with a unique prefix, performs operations on them, and then cleans up after itself.
## Test Runner
The test runner script (`run_all_tests.rhai`) provides a framework for executing all tests and reporting results. It:
1. Checks if Redis is available before running tests
2. Skips tests if Redis is not available
3. Contains simplified versions of each test
4. Runs each test in a try/catch block to handle errors
5. Catches and reports any errors
6. Provides a summary of passed, failed, and skipped tests
## Redis Server Requirements
These tests require a Redis server to be running and accessible. The tests will attempt to connect to Redis using the following strategy:
1. First, try to connect via Unix socket at `$HOME/hero/var/myredis.sock`
2. If that fails, try to connect via TCP to `127.0.0.1` on the default Redis port (6379)
If no Redis server is available, the tests will be skipped rather than failing.
## Authentication Support
The Redis client module will support authentication using the builder pattern in a future update.
The backend implementation is ready, but the Rhai bindings are still in development.
When implemented, the builder pattern will support the following configuration options:
- Host: Set the Redis host
- Port: Set the Redis port
- Database: Set the Redis database number
- Username: Set the Redis username (Redis 6.0+)
- Password: Set the Redis password
- TLS: Enable/disable TLS
- Unix socket: Enable/disable Unix socket
- Socket path: Set the Unix socket path
- Connection timeout: Set the connection timeout in seconds
## Adding New Tests
To add a new test:
1. Create a new Rhai script in the `src/rhai_tests/redisclient` directory
2. Add a new test section to the `run_all_tests.rhai` script
3. Update this documentation to include information about the new test
## Best Practices for Writing Tests
When writing tests for the Redis client module:
1. Always check if Redis is available before running tests
2. Use a unique prefix for test keys to avoid conflicts
3. Clean up any keys created during testing
4. Use assertions to verify expected behavior
5. Print clear messages about what's being tested
6. Handle errors gracefully
7. Make tests independent of each other
8. Keep tests focused on specific functionality

View File

@@ -0,0 +1,113 @@
# RFS Module Tests
This document describes the test scripts for the RFS (Remote File System) module in the SAL library. These tests verify the functionality of the RFS module's mount operations and filesystem layer management.
## Test Structure
The tests are organized into two main scripts:
1. **Mount Operations** (`01_mount_operations.rhai`): Tests for mounting, listing, and unmounting filesystems.
2. **Filesystem Layer Operations** (`02_filesystem_layer_operations.rhai`): Tests for packing, unpacking, listing, and verifying filesystem layers.
Additionally, there's a runner script (`run_all_tests.rhai`) that executes all tests and reports results. The runner script contains simplified versions of the individual tests to avoid dependency issues.
## Running the Tests
To run all tests, execute the following command from the project root:
```bash
herodo --path src/rhai_tests/rfs/run_all_tests.rhai
```
To run individual test scripts:
```bash
herodo --path src/rhai_tests/rfs/01_mount_operations.rhai
```
## Test Details
### Mount Operations Test
The Mount Operations test (`01_mount_operations.rhai`) verifies the following functions:
- `rfs_mount`: Mounting a filesystem
- Tests mounting a local directory with options
- Verifies mount properties (ID, source, target, type)
- `rfs_list_mounts`: Listing mounted filesystems
- Tests listing all mounts
- Verifies that the mounted filesystem is in the list
- `rfs_get_mount_info`: Getting information about a mounted filesystem
- Tests getting information about a specific mount
- Verifies that the mount information is correct
- `rfs_unmount`: Unmounting a specific filesystem
- Tests unmounting a specific mount
- Verifies that the mount is no longer available
- `rfs_unmount_all`: Unmounting all filesystems
- Tests unmounting all mounts
- Verifies that no mounts remain after the operation
The test also verifies that files in the mounted filesystem are accessible and have the correct content.
### Filesystem Layer Operations Test
The Filesystem Layer Operations test (`02_filesystem_layer_operations.rhai`) verifies the following functions:
- `rfs_pack`: Packing a directory into a filesystem layer
- Tests packing a directory with files and subdirectories
- Verifies that the output file is created
- `rfs_list_contents`: Listing the contents of a filesystem layer
- Tests listing the contents of a packed filesystem layer
- Verifies that the list includes all expected files
- `rfs_verify`: Verifying a filesystem layer
- Tests verifying a packed filesystem layer
- Verifies that the layer is valid
- `rfs_unpack`: Unpacking a filesystem layer
- Tests unpacking a filesystem layer to a directory
- Verifies that all files are unpacked correctly with the right content
The test creates a directory structure with files, packs it into a filesystem layer, and then unpacks it to verify the integrity of the process.
## Test Runner
The test runner script (`run_all_tests.rhai`) provides a framework for executing all tests and reporting results. It:
1. Checks if RFS is available before running tests
2. Skips tests if RFS is not available
3. Contains simplified versions of each test
4. Runs each test in a try/catch block to handle errors
5. Catches and reports any errors
6. Provides a summary of passed, failed, and skipped tests
## RFS Requirements
These tests require the RFS tool to be installed and available in the system's PATH. The tests will check for RFS's availability and skip the tests if it's not found, rather than failing.
## Adding New Tests
To add a new test:
1. Create a new Rhai script in the `src/rhai_tests/rfs` directory
2. Add a new test section to the `run_all_tests.rhai` script
3. Update this documentation to include information about the new test
## Best Practices for Writing Tests
When writing tests for the RFS module:
1. Always check if RFS is available before running tests
2. Clean up any mounts before and after testing
3. Use unique names for test directories and files to avoid conflicts
4. Clean up any files or directories created during testing
5. Use assertions to verify expected behavior
6. Print clear messages about what's being tested
7. Handle errors gracefully
8. Make tests independent of each other
9. Keep tests focused on specific functionality

View File

@@ -0,0 +1,76 @@
# Running Rhai Tests
This document describes how to run the Rhai tests for the SAL library.
## Test Structure
The Rhai tests are organized by module in the `src/rhai_tests` directory:
- `src/rhai_tests/os/`: Tests for the OS module
- `src/rhai_tests/git/`: Tests for the Git module
Each module directory contains:
- Individual test scripts (e.g., `01_file_operations.rhai`)
- A test runner script (`run_all_tests.rhai`) that runs all tests for that module
## Running Tests
### Running All Tests
To run all Rhai tests across all modules, use the provided shell script:
```bash
./run_rhai_tests.sh
```
This script:
1. Finds all test runner scripts in the `src/rhai_tests` directory
2. Runs each test runner
3. Reports the results for each module
4. Provides a summary of all test results
The script will exit with code 0 if all tests pass, or code 1 if any tests fail.
### Running Tests for a Specific Module
To run tests for a specific module, use the `herodo` command with the module's test runner:
```bash
herodo --path src/rhai_tests/os/run_all_tests.rhai
```
### Running Individual Tests
To run a specific test, use the `herodo` command with the test script:
```bash
herodo --path src/rhai_tests/os/01_file_operations.rhai
```
## Test Output
The test output includes:
- Information about what's being tested
- Success or failure messages for each test
- A summary of test results
Successful tests are indicated with a checkmark (✓), while failed tests show an error message.
## Adding New Tests
When adding new tests:
1. Create a new test script in the appropriate module directory
2. Update the module's test runner script to include the new test
3. Update the module's documentation to describe the new test
The `run_rhai_tests.sh` script will automatically find and run the new tests as long as they're included in a module's test runner script.
## Troubleshooting
If tests fail, check the following:
1. Make sure the `herodo` binary is in your PATH
2. Verify that the test scripts have the correct permissions
3. Check for any dependencies required by the tests (e.g., `git` for Git module tests)
4. Look for specific error messages in the test output

View File

@@ -0,0 +1,129 @@
# Text Module Tests
This document describes the test scripts for the Text module in the SAL library. These tests verify the functionality of the Text module's text manipulation, normalization, replacement, and template rendering capabilities.
## Test Structure
The tests are organized into four main scripts:
1. **Text Indentation** (`01_text_indentation.rhai`): Tests for the `dedent` and `prefix` functions.
2. **Filename and Path Normalization** (`02_name_path_fix.rhai`): Tests for the `name_fix` and `path_fix` functions.
3. **Text Replacement** (`03_text_replacer.rhai`): Tests for the `TextReplacer` class and its methods.
4. **Template Rendering** (`04_template_builder.rhai`): Tests for the `TemplateBuilder` class and its methods.
Additionally, there's a runner script (`run_all_tests.rhai`) that executes all tests and reports results. The runner script contains simplified versions of the individual tests to avoid dependency issues.
## Running the Tests
To run all tests, execute the following command from the project root:
```bash
herodo --path src/rhai_tests/text/run_all_tests.rhai
```
To run individual test scripts:
```bash
herodo --path src/rhai_tests/text/01_text_indentation.rhai
```
## Test Details
### Text Indentation Test
The text indentation test (`01_text_indentation.rhai`) verifies the following functions:
- `dedent`: Removes common leading whitespace from multiline strings
- Tests basic indentation removal
- Tests mixed indentation handling
- Tests preservation of empty lines
- Tests handling of text without indentation
- Tests single line indentation removal
- `prefix`: Adds a specified prefix to each line of a multiline string
- Tests basic prefix addition
- Tests empty prefix handling
- Tests prefix addition to empty lines
- Tests prefix addition to single line
- Tests non-space prefix addition
- Combination of `dedent` and `prefix` functions
### Filename and Path Normalization Test
The filename and path normalization test (`02_name_path_fix.rhai`) verifies the following functions:
- `name_fix`: Normalizes filenames
- Tests basic name fixing (spaces to underscores, lowercase conversion)
- Tests special character handling
- Tests multiple special character handling
- Tests non-ASCII character removal
- Tests uppercase conversion
- `path_fix`: Applies `name_fix` to the filename portion of a path
- Tests paths ending with `/` (directories)
- Tests single filename handling
- Tests path with filename handling
- Tests relative path handling
- Tests path with special characters in filename
### Text Replacement Test
The text replacement test (`03_text_replacer.rhai`) verifies the following functions:
- `TextReplacer` with simple replacements
- Tests basic replacement
- Tests multiple replacements
- `TextReplacer` with regex replacements
- Tests basic regex replacement
- Tests case-insensitive regex replacement
- `TextReplacer` with file operations
- Tests `replace_file` (read file, apply replacements, return result)
- Tests `replace_file_to` (read file, apply replacements, write to new file)
- Tests `replace_file_in_place` (read file, apply replacements, write back to same file)
### Template Rendering Test
The template rendering test (`04_template_builder.rhai`) verifies the following functions:
- `TemplateBuilder` with file template
- Tests basic template with string variable
- Tests template with multiple variables of different types
- Tests template with array variable
- Tests template with map variable
- `TemplateBuilder` with file operations
- Tests template from file
- Tests `render_to_file` (render template, write to file)
Note: The `template_builder_open` function expects a file path, not a string template. The test creates template files on disk for testing.
## Test Runner
The test runner script (`run_all_tests.rhai`) provides a framework for executing all tests and reporting results. It:
1. Contains simplified versions of each test
2. Runs each test in a try/catch block to handle errors
3. Catches and reports any errors
4. Provides a summary of passed and failed tests
## Adding New Tests
To add a new test:
1. Create a new Rhai script in the `src/rhai_tests/text` directory
2. Add a new test section to the `run_all_tests.rhai` script
3. Update this documentation to include information about the new test
## Best Practices for Writing Tests
When writing tests for the Text module:
1. Use the `assert_true` and `assert_eq` functions to verify expected behavior
2. Print clear messages about what's being tested
3. Clean up any temporary files or directories created during testing
4. Handle errors gracefully
5. Make tests independent of each other
6. Keep tests focused on specific functionality

View File

@@ -0,0 +1,8 @@
{
"label": "SAL",
"position": 6,
"link": {
"type": "generated-index",
"description": "Tools to work with operating system."
}
}

239
docs/docs/sal/buildah.md Normal file
View File

@@ -0,0 +1,239 @@
---
title: "build containers"
sidebar_position: 20
hide_title: true
---
# Container Builder
The Buildah module provides functions for working with containers and images using the Buildah tool. Buildah helps you create and manage container images.
## Builder Pattern
The Buildah module now supports a Builder pattern, which provides a more intuitive and flexible way to work with containers and images.
### Creating a Builder
```js
// Create a builder with a name and base image
let builder = bah_new("my-container", "alpine:latest");
// Access builder properties
let container_id = builder.container_id;
let name = builder.name;
let image = builder.image;
```
### Builder Methods
The Builder object provides the following methods:
- `run(command)`: Run a command in the container
- `run_with_isolation(command, isolation)`: Run a command with specified isolation
- `copy(source, dest)`: Copy files into the container
- `add(source, dest)`: Add files into the container
- `commit(image_name)`: Commit the container to an image
- `remove()`: Remove the container
- `reset()`: Remove the container and clear the container_id
- `config(options)`: Configure container metadata
- `set_entrypoint(entrypoint)`: Set the entrypoint for the container
- `set_cmd(cmd)`: Set the default command for the container
- `debug_mode`: Get or set the debug flag (true/false)
- `write_content(content, dest_path)`: Write content to a file in the container
- `read_content(source_path)`: Read content from a file in the container
- `images()`: List images in local storage
- `image_remove(image)`: Remove an image
- `image_pull(image, tls_verify)`: Pull an image from a registry
- `image_push(image, destination, tls_verify)`: Push an image to a registry
- `image_tag(image, new_name)`: Add a tag to an image
- `build(tag, context_dir, file, isolation)`: Build an image from a Dockerfile
- `build(tag, context_dir, file, isolation)`: Build an image from a Dockerfile
### Example
```js
// Create a builder
let builder = bah_new("my-container", "alpine:latest");
// Enable debug mode to see command output
builder.debug_mode = true;
// Reset the builder to remove any existing container
builder.reset();
// Create a new container
builder = bah_new("my-container", "alpine:latest");
// Run a command
let result = builder.run("echo 'Hello from container'");
println(`Command output: ${result.stdout}`);
// Write content directly to a file in the container
let script_content = `#!/bin/sh
echo "Hello from startup script"
`;
builder.write_content(script_content, "/start.sh");
builder.run("chmod +x /start.sh");
// Set the entrypoint for the container
builder.set_entrypoint("/start.sh");
// Add a file
file_write("test_file.txt", "Test content");
builder.add("test_file.txt", "/");
// Commit to an image
builder.commit("my-custom-image:latest");
// Clean up
builder.remove();
delete("test_file.txt");
```
## Image Information
### Image Properties
When working with images, you can access the following information:
- `id`: The unique identifier for the image
- `names`: A list of names/tags for the image
- `name`: The primary name of the image, or `<none>` if the image has no names
- `size`: The size of the image
- `created`: When the image was created
## Builder Methods
### `bah_new(name, image)`
Creates a new Builder object for working with a container.
**Parameters:**
- `name` (string): The name to give the container
- `image` (string): The name or ID of the image to create the container from
**Returns:** A Builder object if successful.
**Example:**
```js
// Create a new Builder
let builder = bah_new("my-container", "alpine:latest");
```
**Notes:**
- If a container with the given name already exists, it will be reused instead of creating a new one
- The Builder object provides methods for working with the container
### `reset()`
Resets a Builder by removing the container and clearing the container_id. This allows you to start fresh with the same Builder object.
**Returns:** Nothing.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Reset the Builder to remove the container
builder.reset();
// Create a new container with the same name
builder = bah_new("my-container", "alpine:latest");
```
### `debug_mode`
Get or set the debug flag for the Builder. When debug mode is enabled, all buildah commands will output their stdout/stderr, making it easier to debug issues.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Enable debug mode
builder.debug_mode = true;
// Run a command with debug output
builder.run("echo 'Hello with debug'");
// Disable debug mode
builder.debug_mode = false;
```
### `set_entrypoint(entrypoint)`
Sets the entrypoint for the container. The entrypoint is the command that will be executed when the container starts.
**Parameters:**
- `entrypoint` (string): The entrypoint command
**Returns:** Command result if successful.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Set the entrypoint
builder.set_entrypoint("/start.sh");
```
### `set_cmd(cmd)`
Sets the default command for the container. This is used as arguments to the entrypoint.
**Parameters:**
- `cmd` (string): The default command
**Returns:** Command result if successful.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Set the default command
builder.set_cmd("--verbose");
```
### `write_content(content, dest_path)`
Writes content to a file in the container.
**Parameters:**
- `content` (string): The content to write
- `dest_path` (string): The destination path in the container
**Returns:** Command result if successful.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Write content to a file
let content = "Hello, world!";
builder.write_content(content, "/hello.txt");
```
### `read_content(source_path)`
Reads content from a file in the container.
**Parameters:**
- `source_path` (string): The source path in the container
**Returns:** The file content as a string if successful.
**Example:**
```js
// Create a Builder
let builder = bah_new("my-container", "alpine:latest");
// Write content to a file
builder.write_content("Hello, world!", "/hello.txt");
// Read content from the file
let content = builder.read_content("/hello.txt");
println(content); // Outputs: Hello, world!
```

210
docs/docs/sal/git.md Normal file
View File

@@ -0,0 +1,210 @@
---
title: "git"
sidebar_position: 5
hide_title: true
---
# Git
This module provides HeroScript wrappers for the Git functionality in SAL.
> **Note:** The constructor for GitTree has been renamed from `new()` to `gittree_new()` to avoid confusion with other constructors. This makes the interface more explicit and less likely to cause naming conflicts.
## Object-Oriented Design
The Git module follows an object-oriented design with two main classes:
1. **GitTree** - Represents a collection of git repositories under a base path
- Created with `gittree_new(base_path)`
- Methods for listing, finding, and getting repositories
2. **GitRepo** - Represents a single git repository
- Obtained from GitTree's `get()` method
- Methods for common git operations: pull, reset, push, commit
This design allows for a more intuitive and flexible interface, with method chaining for complex operations.
## Creating a GitTree
The GitTree object is the main entry point for git operations. It represents a collection of git repositories under a base path.
```js
// Create a new GitTree with a base path
let git_tree = gittree_new("/root/code");
print(`Created GitTree with base path: /home/user/code`);
```
## Finding Repositories
### List All Repositories
```js
// List all git repositories under the base path
let repos = git_tree.list();
print(`Found ${repos.len()} repositories`);
// Print the repositories
for repo in repos {
print(` - ${repo}`);
}
```
### Find Repositories Matching a Pattern
```js
// Find repositories matching a pattern
// Use a wildcard (*) suffix to find multiple matches
let matching_repos = git_tree.find("my-project*");
print("Matching repositories:");
for repo in matching_repos {
print(` - ${repo}`);
}
// Find a specific repository (must match exactly one)
let specific_repo = git_tree.find("unique-project")[0];
print(`Found specific repository: ${specific_repo}`);
```
## Working with Repositories
### Get Repository Objects
```js
// Get GitRepo objects for repositories matching a pattern
let repos = git_tree.get("my-project*");
print(`Found ${repos.len()} repositories`);
// Get a specific repository
let repo = git_tree.get("unique-project")[0];
print(`Working with repository: ${repo.path()}`);
```
### Clone a Repository
```js
// Clone a repository by URL
// This will clone the repository to the base path of the GitTree
let repos = git_tree.get("https://github.com/username/repo.git");
let repo = repos[0];
print(`Repository cloned to: ${repo.path()}`);
```
### Check for Changes
```js
// Check if a repository has uncommitted changes
let repo = git_tree.get("my-project")[0];
if repo.has_changes() {
print("Repository has uncommitted changes");
} else {
print("Repository is clean");
}
```
## Repository Operations
### Pull Changes
```js
// Pull the latest changes from the remote
// This will fail if there are uncommitted changes
let repo = git_tree.get("my-project")[0];
let result = repo.pull();
print("Repository updated successfully");
```
### Reset Local Changes
```js
// Reset any local changes in the repository
let repo = git_tree.get("my-project")[0];
let result = repo.reset();
print("Repository reset successfully");
```
### Commit Changes
```js
// Commit changes in the repository
let repo = git_tree.get("my-project")[0];
let result = repo.commit("Fix bug in login form");
print("Changes committed successfully");
```
### Push Changes
```js
// Push changes to the remote
let repo = git_tree.get("my-project")[0];
let result = repo.push();
print("Changes pushed successfully");
```
## Method Chaining
The GitRepo methods can be chained together for more complex operations:
```js
// Commit changes and push them to the remote
let repo = git_tree.get("my-project")[0];
let result = repo.commit("Add new feature").push();
print("Changes committed and pushed successfully");
// Reset local changes, pull the latest changes, and commit new changes
let repo = git_tree.get("my-project")[0];
let result = repo.reset().pull().commit("Update dependencies");
print("Repository updated successfully");
```
## Complete Example
```js
// Create a new GitTree
let home_dir = env("HOME");
let git_tree = gittree_new(`${home_dir}/code`);
// Clone a repository
let repos = git_tree.get("https://github.com/username/example-repo.git");
let repo = repos[0];
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 result = repo.commit("Update README.md").push();
print("Changes committed and pushed successfully");
// List all repositories
let all_repos = git_tree.list();
print("All repositories:");
for repo_path in all_repos {
print(` - ${repo_path}`);
}
```
## Error Handling
All methods in the Git module return a Result type, which means they can either succeed or fail with an error. If an error occurs, it will be propagated to the HeroScript script as a runtime error.
For example, if you try to clone a repository that doesn't exist:
```js
// Try to clone a non-existent repository
try {
let git_tree = gittree_new("/root/code");
let repos = git_tree.get("https://github.com/nonexistent/repo.git");
print("This will not be executed if the repository doesn't exist");
} catch(err) {
print(`Error: ${err}`); // Will print the error message from git
}
```
Common errors include:
- Invalid URL
- Repository not found
- Authentication failure
- Network issues
- Local changes exist when trying to pull

215
docs/docs/sal/git/git.md Normal file
View File

@@ -0,0 +1,215 @@
# Rhai Git Module Manual
## Core Concepts
The Git module in Rhai allows interaction with Git repositories through two main objects:
- **`GitTree`**: Represents a collection of Git repositories under a specified base directory. Use it to manage, find, and access these repositories.
- **`GitRepo`**: Represents a single Git repository. Use it to perform common Git operations like `pull`, `commit`, and `push`.
## Error Handling
Methods performing Git operations (e.g., `pull`, `GitTree.get` when cloning) return a `Result`. If an operation fails and the error is not handled within the Rhai script, the script execution will halt, and Rhai will report the error. The examples below show direct usage, relying on this default error-halting behavior.
## `GitTree` Object
The `GitTree` object is the entry point for working with Git repositories.
### `git_tree_new(base_path: String) -> GitTree`
Creates a `GitTree` instance.
- **Description**: Initializes a `GitTree` to operate within the `base_path`. This directory is where repositories are located or will be cloned. It's created if it doesn't exist.
- **Parameters**:
- `base_path: String` - Path to the directory for Git repositories.
- **Returns**: `GitTree` - A new `GitTree` object. Halts on error (e.g., invalid path).
- **Rhai Example**:
```rhai
let git_tree = git_tree_new("./my_projects");
print("GitTree created.");
// To access the base path from Rhai, a `base_path()` getter would need to be exposed.
// // print(`GitTree base path: ${git_tree.base_path()}`);
```
### `list() -> Array`
Lists names of all Git repositories in the `GitTree`'s `base_path`.
- **Description**: Scans `base_path` for immediate subdirectories that are Git repositories and returns their names.
- **Returns**: `Array` - An array of strings (repository names). Returns an empty array if no repositories are found. Halts on other errors.
- **Rhai Example**:
```rhai
let git_tree = git_tree_new("./my_projects");
let repo_names = git_tree.list();
print(`Found ${repo_names.len()} repositories: ${repo_names}`);
```
### `find(pattern: String) -> Array`
Finds Git repositories matching `pattern` and returns them as `GitRepo` objects.
- **Description**: Searches `base_path` for Git repository subdirectories whose names match the `pattern` (e.g., `*`, `service-*`).
- **Parameters**:
- `pattern: String` - A pattern to match repository names.
- **Returns**: `Array` - An array of `GitRepo` objects. Returns an empty array if no repositories match. Halts on other errors (e.g. invalid pattern).
- **Rhai Example**:
```rhai
let git_tree = git_tree_new("./my_projects");
let api_repos = git_tree.find("api-*");
print(`Found ${api_repos.len()} API repositories.`);
for repo in api_repos {
print(`- Path: ${repo.path()}, Has Changes: ${repo.has_changes()}`);
}
```
### `get(name_or_url: String) -> GitRepo`
Retrieves a single `GitRepo` object by its exact local name or by a remote URL.
- **Description**:
- **Local Name**: If `name_or_url` is an exact subdirectory name in `base_path` (e.g., `"myrepo"`), opens that repository.
- **Remote URL**: If `name_or_url` is a Git URL (e.g., `"https://github.com/user/repo.git"`), it clones the repository (if not present) into `base_path` or opens it if it already exists.
- **Note**: Does not support wildcards for local names. Use `find()` for pattern matching.
- **Parameters**:
- `name_or_url: String` - The exact local repository name or a full Git URL.
- **Returns**: `GitRepo` - A single `GitRepo` object.
- **Halts on error if**:
- The local `name` is not found or is ambiguous.
- The `url` is invalid, or the clone/access operation fails.
- The target is not a valid Git repository.
- **Rhai Examples**:
*Get specific local repository by name:*
```rhai
let git_tree = git_tree_new("./my_projects");
// Assumes "my_service_a" is a git repo in "./my_projects/my_service_a"
// Script halts if "my_service_a" is not found or not a git repo.
let service_a_repo = git_tree.get("my_service_a");
print(`Opened repo: ${service_a_repo.path()}`);
service_a_repo.pull(); // Example operation
```
*Clone or get repository by URL:*
```rhai
let git_tree = git_tree_new("./cloned_repos_dest");
let url = "https://github.com/rhai-script/rhai.git";
// Clones if not present, otherwise opens. Halts on error.
let rhai_repo = git_tree.get(url);
print(`Rhai repository path: ${rhai_repo.path()}`);
print(`Rhai repo has changes: ${rhai_repo.has_changes()}`);
```
## `GitRepo` Object
Represents a single Git repository. Obtained from `GitTree.get()` or `GitTree.find()`.
### `path() -> String`
Returns the full file system path of the repository.
- **Returns**: `String` - The absolute path to the repository's root directory.
- **Rhai Example**:
```rhai
let git_tree = git_tree_new("./my_projects");
// Assumes "my_app" exists and is a Git repository.
// get() will halt if "my_app" is not found.
let app_repo = git_tree.get("my_app");
print(`App repository is at: ${app_repo.path()}`);
```
### `has_changes() -> bool`
Checks if the repository has any uncommitted local changes.
- **Description**: Checks for uncommitted modifications in the working directory or staged changes.
- **Returns**: `bool` - `true` if uncommitted changes exist, `false` otherwise. Halts on error.
- **Rhai Example** (assuming `app_repo` is a `GitRepo` object):
```rhai
if app_repo.has_changes() {
print(`Repository ${app_repo.path()} has uncommitted changes.`);
} else {
print(`Repository ${app_repo.path()} is clean.`);
}
```
### `pull() -> GitRepo`
Pulls latest changes from the remote.
- **Description**: Fetches changes from the default remote and merges them into the current local branch (`git pull`).
- **Returns**: `GitRepo` - The same `GitRepo` object for chaining. Halts on error (e.g., network issues, merge conflicts).
- **Rhai Example** (assuming `app_repo` is a `GitRepo` object):
```rhai
print(`Pulling latest changes for ${app_repo.path()}...`);
app_repo.pull(); // Halts on error
print("Pull successful.");
```
### `reset() -> GitRepo`
Resets local changes. **Caution: Discards uncommitted work.**
- **Description**: Discards local modifications and staged changes, resetting the working directory to match the last commit (`git reset --hard HEAD` or similar).
- **Returns**: `GitRepo` - The same `GitRepo` object for chaining. Halts on error.
- **Rhai Example** (assuming `app_repo` is a `GitRepo` object):
```rhai
print(`Resetting local changes in ${app_repo.path()}...`);
app_repo.reset(); // Halts on error
print("Reset successful.");
```
### `commit(message: String) -> GitRepo`
Commits staged changes.
- **Description**: Performs `git commit -m "message"`. Assumes changes are staged. Behavior regarding auto-staging of tracked files depends on the underlying Rust implementation.
- **Parameters**:
- `message: String` - The commit message.
- **Returns**: `GitRepo` - The same `GitRepo` object for chaining. Halts on error (e.g., nothing to commit).
- **Rhai Example** (assuming `app_repo` is a `GitRepo` object):
```rhai
// Ensure there are changes to commit.
if app_repo.has_changes() {
print(`Committing changes in ${app_repo.path()}...`);
app_repo.commit("Automated commit via Rhai script"); // Halts on error
print("Commit successful.");
} else {
print("No changes to commit.");
}
```
### `push() -> GitRepo`
Pushes committed changes to the remote.
- **Description**: Performs `git push` to the default remote and branch.
- **Returns**: `GitRepo`
```rhai
print(`Pushing changes for ${app_repo.path()}...`);
app_repo.push(); // Halts on error
print("Push successful.");
```
## Chaining Operations
```rhai
let git_tree = git_tree_new("./my_projects");
// Assumes "my_writable_app" exists and you have write access.
// get() will halt if not found.
let app_repo = git_tree.get("my_writable_app");
print(`Performing chained operations on ${app_repo.path()}`);
// This example demonstrates a common workflow.
// Ensure the repo state is suitable (e.g., changes exist for commit/push).
app_repo.pull()
.commit("Rhai: Chained operations - automated update") // Commits if pull results in changes or local changes existed and were staged.
.push();
print("Chained pull, commit, and push reported successful.");
// Alternative:
// app_repo.pull();
// if app_repo.has_changes() {
// app_repo.commit("Updates").push();
// }
```

10
docs/docs/sal/intro.md Normal file
View File

@@ -0,0 +1,10 @@
---
title: "intro"
sidebar_position: 1
hide_title: true
---
## HeroScript Script Commands Documentation
The SAL library provides integration with the HeroScript scripting language, allowing you to use powerful system functions within your HeroScript scripts. These functions are organized into modules that provide related functionality.

239
docs/docs/sal/nerdctl.md Normal file
View File

@@ -0,0 +1,239 @@
---
title: "run containers"
sidebar_position: 21
hide_title: true
---
# Container Manager
The Container Manager module provides a comprehensive API for working with containers using nerdctl. It offers a modern builder pattern approach for container management.
## Container Builder Pattern
The Container Builder Pattern allows for fluent, chainable configuration of containers. This pattern makes container creation more readable and maintainable by allowing you to build complex container configurations step by step.
### Creating a Container
Start by creating a new container instance:
```rhai
// Create an empty container with just a name
let container = nerdctl_container_new("my-container");
// Or create a container from an image
let container = nerdctl_container_from_image("my-container", "nginx:latest");
```
### Configuring the Container
Once you have a container instance, you can configure it using the various builder methods:
```rhai
// Configure the container with various options
let container = nerdctl_container_from_image("web-server", "nginx:latest")
.with_port("8080:80") // Map port 8080 to container port 80
.with_volume("/host/path:/container/path") // Mount a volume
.with_env("NGINX_HOST", "localhost") // Set an environment variable
.with_network("bridge") // Set the network
.with_detach(true); // Run in detached mode
```
### Resetting Container Configuration
If you need to reset the container configuration to its default state while keeping the name and image:
```rhai
// Reset the container configuration
let container = nerdctl_container_from_image("web-server", "nginx:latest")
.reset() // Reset all configuration to defaults
.with_port("8080:80") // Start configuring again
.with_detach(true);
```
### Building and Starting the Container
After configuring the container, you can build and start it:
```rhai
// Build the container (creates it but doesn't start it)
let built_container = container.build();
// Start the container
let start_result = built_container.start();
// Check if the container started successfully
if (start_result.success) {
println("Container started successfully!");
} else {
println(`Failed to start container: ${start_result.stderr}`);
}
```
### Container Lifecycle Operations
Once your container is running, you can perform various operations:
```rhai
// Execute a command in the container
let exec_result = container.exec("ls -la");
// Get container logs
let logs = container.logs();
// Stop the container
let stop_result = container.stop();
// Remove the container
let remove_result = container.remove();
```
## Available Builder Methods
The Container Builder Pattern provides the following methods for configuring containers:
| Method | Description | Example |
| -------------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------- |
| `reset()` | Reset configuration to defaults | `.reset()` |
| `with_port(port)` | Add a port mapping | `.with_port("8080:80")` |
| `with_ports(ports_array)` | Add multiple port mappings | `.with_ports(["8080:80", "443:443"])` |
| `with_volume(volume)` | Add a volume mount | `.with_volume("/host/path:/container/path")` |
| `with_volumes(volumes_array)` | Add multiple volume mounts | `.with_volumes(["/host/path1:/container/path1", "/host/path2:/container/path2"])` |
| `with_env(key, value)` | Add an environment variable | `.with_env("NGINX_HOST", "localhost")` |
| `with_envs(env_map)` | Add multiple environment variables | `.with_envs(#{"KEY1": "value1", "KEY2": "value2"})` |
| `with_network(network)` | Set the network | `.with_network("bridge")` |
| `with_network_alias(alias)` | Add a network alias | `.with_network_alias("web-server")` |
| `with_network_aliases(aliases_array)` | Add multiple network aliases | `.with_network_aliases(["web-server", "http-service"])` |
| `with_cpu_limit(cpus)` | Set CPU limit | `.with_cpu_limit("1.0")` |
| `with_cpu_shares(shares)` | Set CPU shares | `.with_cpu_shares("1024")` |
| `with_memory_limit(memory)` | Set memory limit | `.with_memory_limit("512m")` |
| `with_memory_swap_limit(memory_swap)` | Set memory swap limit | `.with_memory_swap_limit("1g")` |
| `with_restart_policy(policy)` | Set restart policy | `.with_restart_policy("unless-stopped")` |
| `with_health_check(cmd)` | Set health check command | `.with_health_check("curl -f http://localhost/ | | exit 1")` |
| `with_health_check_options(cmd, interval, timeout, retries, start_period)` | Set health check with options | `.with_health_check_options("curl -f http://localhost/ | | exit 1", "5s", "3s", 3, "10s")` |
| `with_snapshotter(snapshotter)` | Set snapshotter | `.with_snapshotter("native")` |
| `with_detach(detach)` | Set detach mode | `.with_detach(true)` |
## Complete Example: Web Server
Here's a complete example that demonstrates setting up an Nginx web server using the Container Builder Pattern:
```rhai
// Create a temporary directory for our files
let work_dir = "/tmp/nerdctl";
mkdir(work_dir);
chdir(work_dir);
// Create a custom index.html file
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>
</body>
</html>
`;
// Write the HTML file
let html_file = `${work_dir}/index.html`;
file_write(html_file, html_content);
// Set up environment variables
let env_map = #{};
env_map["NGINX_HOST"] = "localhost";
env_map["NGINX_PORT"] = "80";
env_map["NGINX_WORKER_PROCESSES"] = "auto";
// Create and configure the container
let container_name = "rhai-nginx-demo";
// First, try to remove any existing container with the same name
nerdctl_remove(container_name);
// Create a container with a rich set of options using the builder pattern
let container = nerdctl_container_from_image(container_name, "nginx:latest")
.reset() // Reset to default configuration
.with_detach(true)
.with_ports(["8080:80"]) // Add multiple ports at once
.with_volumes([`${work_dir}:/usr/share/nginx/html`]) // Mount our work dir
.with_envs(env_map) // Add multiple environment variables at once
.with_network("bridge")
.with_network_aliases(["web-server", "nginx-demo"]) // Add multiple network aliases
.with_cpu_limit("1.0")
.with_memory_limit("512m");
// Build and start the container
let built_container = container.build();
let start_result = built_container.start();
println("The web server is running at http://localhost:8080");
```
## Using Local Images Created with Buildah
When working with images created by Buildah, you may need to take additional steps to ensure nerdctl can find and use these images. This is because Buildah and nerdctl may use different storage backends by default.
### Tagging with localhost Prefix
One approach is to tag the Buildah-created image with a `localhost/` prefix:
```rhai
// Create and commit a container with Buildah
let builder = bah_new("my-container", "alpine:latest");
builder.run("echo 'Hello' > /hello.txt");
builder.commit("my-custom-image:latest");
// Tag the image with localhost prefix for nerdctl compatibility
let local_image_name = "localhost/my-custom-image:latest";
bah_image_tag("my-custom-image:latest", local_image_name);
// Now use the image with nerdctl
let container = nerdctl_container_from_image("my-app", local_image_name)
.with_detach(true)
.build();
```
### Using a Local Registry
For more reliable interoperability, you can push the image to a local registry:
```rhai
// Push the Buildah-created image to a local registry
bah_image_push("my-custom-image:latest", "localhost:5000/my-custom-image:latest", false);
// Pull the image with nerdctl
nerdctl_image_pull("localhost:5000/my-custom-image:latest");
// Use the image
let container = nerdctl_container_from_image("my-app", "localhost:5000/my-custom-image:latest")
.with_detach(true)
.build();
```
## Image Management Functions
The module also provides functions for managing container images:
| Function | Description | Example |
| --------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------- |
| `nerdctl_images()` | List images in local storage | `nerdctl_images()` |
| `nerdctl_image_remove(image)` | Remove an image | `nerdctl_image_remove("nginx:latest")` |
| `nerdctl_image_push(image, destination)` | Push an image to a registry | `nerdctl_image_push("my-image:latest", "registry.example.com/my-image:latest")` |
| `nerdctl_image_tag(image, new_name)` | Add an additional name to a local image | `nerdctl_image_tag("nginx:latest", "my-nginx:latest")` |
| `nerdctl_image_pull(image)` | Pull an image from a registry | `nerdctl_image_pull("nginx:latest")` |
| `nerdctl_image_commit(container, image_name)` | Commit a container to an image | `nerdctl_image_commit("web-server", "my-nginx:latest")` |
| `nerdctl_image_build(tag, context_path)` | Build an image using a Dockerfile | `nerdctl_image_build("my-image:latest", "./")` |

359
docs/docs/sal/os.md Normal file
View File

@@ -0,0 +1,359 @@
---
title: "os"
sidebar_position: 2
hide_title: true
---
# OS Tools
The OS module provides functions for working with files, directories, and downloading files from the internet.
## File System Functions
### `copy(src, dest)`
Recursively copies a file or directory from source to destination.
**Parameters:**
- `src` (string): The source file or directory path
- `dest` (string): The destination path
**Returns:** A message confirming the copy was successful.
**Example:**
```js
// Copy a file
copy("source.txt", "destination.txt");
// Copy a directory recursively
copy("source_dir", "destination_dir");
```
### `exist(path)`
Checks if a file or directory exists.
**Parameters:**
- `path` (string): The path to check
**Returns:** A boolean value - `true` if the file or directory exists, `false` otherwise.
**Example:**
```js
if exist("config.json") {
// File exists, do something
} else {
// File doesn't exist
}
```
### `find_file(dir, filename)`
Finds a file in a directory with support for wildcards.
**Parameters:**
- `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.
**Example:**
```js
// Find a specific file
let config_file = find_file("./config", "settings.json");
// Find using wildcards
let log_file = find_file("./logs", "*.log");
```
### `find_files(dir, filename)`
Finds multiple files in a directory recursively with support for wildcards.
**Parameters:**
- `dir` (string): The directory to search in
- `filename` (string): The filename pattern to search for (supports wildcards)
**Returns:** A list of matching file paths.
**Example:**
```js
// Find all JSON files
let json_files = find_files("./data", "*.json");
// Process each file
for file in json_files {
print(`Found file: ${file}`);
}
```
### `find_dir(dir, dirname)`
Finds a directory in a parent directory with support for wildcards.
**Parameters:**
- `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.
**Example:**
```js
// Find a specific directory
let config_dir = find_dir("./", "config");
// Find using wildcards
let version_dir = find_dir("./releases", "v*");
```
### `find_dirs(dir, dirname)`
Finds multiple directories in a parent directory recursively with support for wildcards.
**Parameters:**
- `dir` (string): The parent directory to search in
- `dirname` (string): The directory name pattern to search for (supports wildcards)
**Returns:** A list of matching directory paths.
**Example:**
```js
// Find all version directories
let version_dirs = find_dirs("./releases", "v*");
// Process each directory
for dir in version_dirs {
print(`Found directory: ${dir}`);
}
```
### `delete(path)`
Deletes a file or directory. This function is defensive and doesn't error if the file doesn't exist.
**Parameters:**
- `path` (string): The path of the file or directory to delete
**Returns:** A message confirming the deletion was successful.
**Example:**
```js
// Delete a file
delete("temp.txt");
// Delete a directory
delete("temp_dir");
```
### `mv(src, dest)`
Moves a file or directory from source to destination.
**Parameters:**
- `src` (string): The source path
- `dest` (string): The destination path
**Returns:** A message confirming the move was successful.
**Example:**
```js
// Move a file
mv("file.txt", "new_location/file.txt");
// Move a directory
mv("source_dir", "destination_dir");
// Rename a file
mv("old_name.txt", "new_name.txt");
```
### `mkdir(path)`
Creates a directory and all parent directories. This function is defensive and doesn't error if the directory already exists.
**Parameters:**
- `path` (string): The path of the directory to create
**Returns:** A message confirming the directory was created.
**Example:**
```js
// Create a directory
mkdir("new_dir");
// Create nested directories
mkdir("parent/child/grandchild");
```
### `file_size(path)`
Gets the size of a file in bytes.
**Parameters:**
- `path` (string): The path of the file
**Returns:** The size of the file in bytes.
**Example:**
```js
// Get file size
let size = file_size("large_file.dat");
print(`File size: ${size} bytes`);
```
## File Content Functions
### `file_read(path)`
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.
**Example:**
```js
// Read a file
let content = file_read("config.json");
print(`File content: ${content}`);
```
### `file_write(path, content)`
Writes content to a file. Creates the file if it doesn't exist, overwrites if it does.
**Parameters:**
- `path` (string): The path of the file to write to
- `content` (string): The content to write to the file
**Returns:** A message confirming the file was written.
**Example:**
```js
// Write to a file
file_write("config.json", "{\n \"setting\": \"value\"\n}");
```
### `file_write_append(path, content)`
Appends content to a file. Creates the file if it doesn't exist.
**Parameters:**
- `path` (string): The path of the file to append to
- `content` (string): The content to append to the file
**Returns:** A message confirming the content was appended.
**Example:**
```js
// Append to a log file
file_write_append("log.txt", "New log entry\n");
```
### `rsync(src, dest)`
Syncs directories using rsync (or platform equivalent).
**Parameters:**
- `src` (string): The source directory
- `dest` (string): The destination directory
**Returns:** A message confirming the directories were synced.
**Example:**
```js
// Sync directories
rsync("source_dir", "backup_dir");
```
### `chdir(path)`
Changes the current working directory.
**Parameters:**
- `path` (string): The path to change to
**Returns:** A message confirming the directory was changed.
**Example:**
```js
// Change directory
chdir("project/src");
```
## Download Functions
### `download(url, dest, min_size_kb)`
Downloads a file from a URL to a destination directory using the curl command. If the URL ends with a supported archive format, the file will be automatically extracted to the destination directory.
**Supported archive formats for automatic extraction:**
- `.tar.gz`
- `.tgz`
- `.tar`
- `.zip`
**Parameters:**
- `url` (string): The URL to download from
- `dest` (string): The destination directory where the file will be saved or extracted
- `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation)
**Returns:** The path where the file was saved or extracted.
**Example:**
```js
// Download a file to a directory
download("https://example.com/file.zip", "downloads/", 10);
```
### `download_file(url, dest, min_size_kb)`
Downloads a file from a URL to a specific file destination using the curl command. This function is designed for downloading files to a specific path, not for extracting archives.
**Parameters:**
- `url` (string): The URL to download from
- `dest` (string): The destination file path where the file will be saved
- `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation)
**Returns:** The path where the file was saved.
**Example:**
```js
// Download a file to a specific path
download_file("https://example.com/file.txt", "downloads/myfile.txt", 10);
```
### `download_install(url, min_size_kb)`
Downloads a file and installs it if it's a supported package format.
**Supported package formats for automatic installation:**
- `.deb` packages on Debian-based systems
**Parameters:**
- `url` (string): The URL to download from
- `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation)
**Returns:** The path where the file was saved or installed.
**Example:**
```js
// Download and install a package
download_install("https://example.com/package.deb", 1000);
```
### `chmod_exec(path)`
Makes a file executable (equivalent to `chmod +x` in Unix).
**Parameters:**
- `path` (string): The path to the file to make executable
**Returns:** A message confirming the file was made executable.
**Example:**
```js
// Make a file executable
chmod_exec("downloads/script.sh");
```

View File

@@ -0,0 +1,60 @@
# os.download Module
### `download(url, dest, min_size_kb)`
Download a file from URL to destination using the curl command.
- **Description**: Downloads a file from the given `url`. If `dest` is a directory, the filename is derived from the URL. If `dest` is a file path, it is used directly. Requires the `curl` command to be available. Halts script execution on download or file writing errors, or if the downloaded file size is less than `min_size_kb`. Returns the destination path.
- **Returns**: `String` - The path where the file was downloaded.
- **Arguments**:
- `url`: `String` - The URL of the file to download.
- `dest`: `String` - The destination path (directory or file).
- `min_size_kb`: `Integer` - The minimum expected size of the downloaded file in kilobytes.
```rhai
let download_url = "https://example.com/archive.zip";
let download_dest_dir = "/tmp/downloads";
print(`Downloading ${download_url} to ${download_dest_dir}...`);
let downloaded_file_path = os::download(download_url, download_dest_dir, 50); // Halts on error
print(`Downloaded to: ${downloaded_file_path}`);
```
---
### `download_file(url, dest, min_size_kb)`
Download a file from URL to a specific file destination using the curl command.
- **Description**: Downloads a file from the given `url` directly to the specified file `dest`. Requires the `curl` command. Halts script execution on download or file writing errors, or if the downloaded file size is less than `min_size_kb`. Returns the destination path.
- **Returns**: `String` - The path where the file was downloaded.
- **Arguments**:
- `url`: `String` - The URL of the file to download.
- `dest`: `String` - The full path where the file should be saved.
- `min_size_kb`: `Integer` - The minimum expected size of the downloaded file in kilobytes.
```rhai
let data_url = "https://example.com/dataset.tar.gz";
let local_path = "/opt/data/dataset.tar.gz";
print(`Downloading ${data_url} to ${local_path}...`);
os::download_file(data_url, local_path, 1024); // Halts on error
print(`Downloaded dataset to: ${local_path}`);
```
---
### `download_install(url, min_size_kb)`
Download a file and install it if it\'s a supported package format.
- **Description**: Downloads a file from the given `url` to a temporary location and then attempts to install it using the appropriate system package manager if the file format is supported (e.g., `.deb` on Ubuntu, `.pkg` or `.dmg` on MacOS). Requires the `curl` command and the system package manager. Halts script execution on download, installation, or file size errors. Returns a success message.
- **Returns**: `String` - A success message upon successful download and installation attempt.
- **Arguments**:
- `url`: `String` - The URL of the package file to download and install.
- `min_size_kb`: `Integer` - The minimum expected size of the downloaded file in kilobytes.
```rhai
let package_url = "https://example.com/mytool.deb";
print(`Downloading and installing ${package_url}...`);
os::download_install(package_url, 300); // Halts on error
print("Installation attempt finished.");
```

338
docs/docs/sal/os/fs.md Normal file
View File

@@ -0,0 +1,338 @@
# os.fs Module
The `os` module provides functions for interacting with the operating system, including file system operations, command execution checks, downloads, and package management.
All functions that interact with the file system or external commands will halt the script execution if an error occurs, unless explicitly noted otherwise.
---
### `copy(src, dest)`
Recursively copy a file or directory from source to destination.
- **Description**: Performs a recursive copy operation. Halts script execution on failure.
- **Returns**: `String` - The destination path.
- **Arguments**:
- `src`: `String` - The path to the source file or directory.
- `dest`: `String` - The path to the destination file or directory.
```rhai
print("Copying directory...");
let source_dir = "/tmp/source_data";
let dest_dir = "/backup/source_data";
let copied_path = os::copy(source_dir, dest_dir); // Halts on error
print(`Copied ${source_dir} to ${copied_path}`);
```
---
### `exist(path)`
Check if a file or directory exists.
- **Description**: Checks for the presence of a file or directory at the given path. This function does NOT halt on error if the path is invalid or permissions prevent checking.
- **Returns**: `Boolean` - `true` if the path exists, `false` otherwise.
- **Arguments**:
- `path`: `String` - The path to check.
```rhai
if os::exist("config.json") {
print(`${file_path} exists.`);
} else {
print(`${file_path} does not exist.`);
}
```
---
### `find_file(dir, filename)`
Find a file in a directory (with support for wildcards).
- **Description**: Searches for a file matching `filename` within the specified `dir`. Supports simple wildcards like `*` and `?`. Halts script execution if the directory cannot be read or if no file is found.
- **Returns**: `String` - The path to the first file found that matches the pattern.
- **Arguments**:
- `dir`: `String` - The directory to search within.
- `filename`: `String` - The filename pattern to search for (e.g., `"*.log"`).
```rhai
let log_file = os::find_file("/var/log", "syslog*.log"); // Halts if not found or directory error
print(`Found log file: ${log_file}`);
```
---
### `find_files(dir, filename)`
Find multiple files in a directory (recursive, with support for wildcards).
- **Description**: Recursively searches for all files matching `filename` within the specified `dir` and its subdirectories. Supports simple wildcards. Halts script execution if the directory cannot be read.
- **Returns**: `Array` of `String` - An array containing paths to all matching files.
- **Arguments**:
- `dir`: `String` - The directory to start the recursive search from.
- `filename`: `String` - The filename pattern to search for (e.g., `"*.tmp"`).
```rhai
let temp_files = os::find_files("/tmp", "*.swp"); // Halts on directory error
print("Found temporary files:");
for file in temp_files {
print(`- ${file}`);
}
```
---
### `find_dir(dir, dirname)`
Find a directory in a parent directory (with support for wildcards).
- **Description**: Searches for a directory matching `dirname` within the specified `dir`. Supports simple wildcards. Halts script execution if the directory cannot be read or if no directory is found.
- **Returns**: `String` - The path to the first directory found that matches the pattern.
- **Arguments**:
- `dir`: `String` - The directory to search within.
- `dirname`: `String` - The directory name pattern to search for (e.g., `"backup_*"`).
```rhai
let latest_backup_dir = os::find_dir("/mnt/backups", "backup_20*"); // Halts if not found or directory error
print(`Found backup directory: ${latest_backup_dir}`);
```
---
### `find_dirs(dir, dirname)`
Find multiple directories in a parent directory (recursive, with support for wildcards).
- **Description**: Recursively searches for all directories matching `dirname` within the specified `dir` and its subdirectories. Supports simple wildcards. Halts script execution if the directory cannot be read.
- **Returns**: `Array` of `String` - An array containing paths to all matching directories.
- **Arguments**:
- `dir`: `String` - The directory to start the recursive search from.
- `dirname`: `String` - The directory name pattern to search for (e.g., `"project_*_v?"`).
```rhai
let project_versions = os::find_dirs("/home/user/dev", "project_*_v?"); // Halts on directory error
print("Found project version directories:");
for dir in project_versions {
print(`- ${dir}`);
}
```
---
### `delete(path)`
Delete a file or directory (defensive - doesn't error if file doesn't exist).
- **Description**: Deletes the file or directory at the given path. If the path does not exist, the function does nothing and does not halt. Halts script execution on other errors (e.g., permission denied, directory not empty). Returns the path that was attempted to be deleted.
- **Returns**: `String` - The path that was given as input.
- **Arguments**:
- `path`: `String` - The path to the file or directory to delete.
```rhai
let temp_path = "/tmp/temporary_item";
print(`Attempting to delete: ${temp_path}`);
os::delete(temp_path); // Halts on permissions or non-empty directory error
print("Deletion attempt finished.");
```
---
### `mkdir(path)`
Create a directory and all parent directories (defensive - doesn't error if directory exists).
- **Description**: Creates the directory at the given path, including any necessary parent directories. If the directory already exists, the function does nothing and does not halt. Halts script execution on other errors (e.g., permission denied). Returns the path that was created (or already existed).
- **Returns**: `String` - The path that was created or checked.
- **Arguments**:
- `path`: `String` - The path to the directory to create.
```rhai
let new_dir = "/data/processed/reports";
print(`Ensuring directory exists: ${new_dir}`);
os::mkdir(new_dir); // Halts on permission error
print("Directory check/creation finished.");
```
---
### `file_size(path)`
Get the size of a file in bytes.
- **Description**: Returns the size of the file at the given path. Halts script execution if the file does not exist or cannot be accessed.
- **Returns**: `Integer` - The size of the file in bytes (as i64).
- **Arguments**:
- `path`: `String` - The path to the file.
```rhai
let file_path = "important_document.pdf";
let size = os::file_size(file_path); // Halts if file not found or cannot read
print(`File size: ${size} bytes`);
```
---
### `rsync(src, dest)`
Sync directories using rsync (or platform equivalent).
- **Description**: Synchronizes the contents of the source directory (`src`) to the destination directory (`dest`) using the system's available rsync-like command. Halts script execution on any error during the sync process. Returns a success message string.
- **Returns**: `String` - A success message indicating the operation completed.
- **Arguments**:
- `src`: `String` - The source directory.
- `dest`: `String` - The destination directory.
```rhai
let source = "/local/project_files";
let destination = "/remote/backup/project_files";
print(`Syncing from ${source} to ${destination}...`);
let result_message = os::rsync(source, destination); // Halts on error
print(`Sync successful: ${result_message}`);
```
---
### `chdir(path)`
Change the current working directory.
- **Description**: Changes the current working directory of the script process. Halts script execution if the directory does not exist or cannot be accessed. Returns the new current working directory path.
- **Returns**: `String` - The absolute path of the directory the process changed into.
- **Arguments**:
- `path`: `String` - The path to change the working directory to.
```rhai
print(`Current directory: ${os::chdir(".")}`); // Use "." to get current path
let new_cwd = "/tmp";
os::chdir(new_cwd); // Halts if directory not found or access denied
print(`Changed directory to: ${os::chdir(".")}`);
```
---
### `file_read(path)`
Read the contents of a file.
- **Description**: Reads the entire content of the file at the given path into a string. Halts script execution if the file does not exist or cannot be read.
- **Returns**: `String` - The content of the file.
- **Arguments**:
- `path`: `String` - The path to the file.
```rhai
let config_content = os::file_read("settings.conf"); // Halts if file not found or cannot read
print("Config content:");
print(config_content);
```
---
### `file_write(path, content)`
Write content to a file (creates the file if it doesn\'t exist, overwrites if it does).
- **Description**: Writes the specified `content` to the file at the given `path`. If the file exists, its content is replaced. If it doesn't exist, it is created. Halts script execution on error (e.g., permission denied, invalid path). Returns the path written to.
- **Returns**: `String` - The path of the file written to.
- **Arguments**:
- `path`: `String` - The path to the file.
- `content`: `String` - The content to write to the file.
```rhai
let output_path = "/tmp/hello.txt";
let text_to_write = "Hello from Rhai!";
os::file_write(output_path, text_to_write); // Halts on error
print(`Wrote to ${output_path}`);
```
---
### `file_write_append(path, content)`
Append content to a file (creates the file if it doesn\'t exist).
- **Description**: Appends the specified `content` to the end of the file at the given `path`. If the file does not exist, it is created. Halts script execution on error (e.g., permission denied, invalid path). Returns the path written to.
- **Returns**: `String` - The path of the file written to.
- **Arguments**:
- `path`: `String` - The path to the file.
- `content`: `String` - The content to append to the file.
```rhai
let log_path = "application.log";
let log_entry = "User login failed.\n";
os::file_write_append(log_path, log_entry); // Halts on error
print(`Appended to ${log_path}`);
```
---
### `mv(src, dest)`
Move a file or directory from source to destination.
- **Description**: Moves the file or directory from `src` to `dest`. Halts script execution on error (e.g., permission denied, source not found, destination exists and cannot be overwritten). Returns the destination path.
- **Returns**: `String` - The path of the destination.
- **Arguments**:
- `src`: `String` - The path to the source file or directory.
- `dest`: `String` - The path to the destination.
```rhai
let old_path = "/tmp/report.csv";
let new_path = "/archive/reports/report_final.csv";
os::mv(old_path, new_path); // Halts on error
print(`Moved ${old_path} to ${new_path}`);
```
---
### `which(command)`
Check if a command exists in the system PATH.
- **Description**: Searches the system's PATH environment variable for the executable `command`. This function does NOT halt on error; it returns an empty string if the command is not found.
- **Returns**: `String` - The full path to the command executable if found, otherwise an empty string (`""`).
- **Arguments**:
- `command`: `String` - The name of the command to search for (e.g., `"git"`).
```rhai
let git_path = os::which("git");
if git_path != "" {
print(`Git executable found at: ${git_path}`);
} else {
print("Git executable not found in PATH.");
}
```
---
### `cmd_ensure_exists(commands)`
Ensure that one or more commands exist in the system PATH.
- **Description**: Checks if all command names specified in the `commands` string (space or comma separated) exist in the system's PATH. Halts script execution if any of the commands are not found. Returns a success message if all commands are found.
- **Returns**: `String` - A success message.
- **Arguments**:
- `commands`: `String` - A string containing one or more command names, separated by spaces or commas (e.g., `"curl,tar,unzip"`).
```rhai
print("Ensuring required commands are available...");
os::cmd_ensure_exists("git curl docker"); // Halts if any command is missing
print("All required commands found.");
```
---
### `chmod_exec(path)`
Make a file executable (equivalent to chmod +x).
- **Description**: Sets the executable permission for the file at the given `path` for the owner, group, and others. Halts script execution on error (e.g., file not found, permission denied). Returns the path modified.
- **Returns**: `String` - The path of the file whose permissions were modified.
- **Arguments**:
- `path`: `String` - The path to the file.
```rhai
let script_path = "/usr/local/bin/myscript";
print(`Making ${script_path} executable...`);
os::chmod_exec(script_path); // Halts on error
print("Permissions updated.");
```

157
docs/docs/sal/os/package.md Normal file
View File

@@ -0,0 +1,157 @@
# os.package Module
### `package_install(package)`
Install a package using the system package manager.
- **Description**: Installs the specified `package` using the detected system package manager (e.g., `apt` on Ubuntu, `brew` on MacOS). Halts script execution if the package manager command fails. Returns a success message.
- **Returns**: `String` - A message indicating successful installation.
- **Arguments**:
- `package`: `String` - The name of the package to install (e.g., `"nano"`).
```rhai
print("Installing 'nano' package...");
os::package_install("nano"); // Halts on package manager error
print("'nano' installed successfully.");
```
---
### `package_remove(package)`
Remove a package using the system package manager.
- **Description**: Removes the specified `package` using the detected system package manager. Halts script execution if the package manager command fails. Returns a success message.
- **Returns**: `String` - A message indicating successful removal.
- **Arguments**:
- `package`: `String` - The name of the package to remove (e.g., `"htop"`).
```rhai
print("Removing 'htop' package...");
os::package_remove("htop"); // Halts on package manager error
print("'htop' removed successfully.");
```
---
### `package_update()`
Update package lists using the system package manager.
- **Description**: Updates the package lists that the system package manager uses (e.g., `apt update`, `brew update`). Halts script execution if the package manager command fails. Returns a success message.
- **Returns**: `String` - A message indicating successful update.
- **Arguments**: None.
```rhai
print("Updating package lists...");
os::package_update(); // Halts on package manager error
print("Package lists updated.");
```
---
### `package_upgrade()`
Upgrade installed packages using the system package manager.
- **Description**: Upgrades installed packages using the detected system package manager (e.g., `apt upgrade`, `brew upgrade`). Halts script execution if the package manager command fails. Returns a success message.
- **Returns**: `String` - A message indicating successful upgrade.
- **Arguments**: None.
```rhai
print("Upgrading installed packages...");
os::package_upgrade(); // Halts on package manager error
print("Packages upgraded.");
```
---
### `package_list()`
List installed packages using the system package manager.
- **Description**: Lists the names of packages installed on the system using the detected package manager. Halts script execution if the package manager command fails.
- **Returns**: `Array` of `String` - An array containing the names of installed packages.
- **Arguments**: None.
```rhai
print("Listing installed packages...");
let installed_packages = os::package_list(); // Halts on package manager error
for pkg in installed_packages {
print(`- ${pkg}`);
}
```
---
### `package_search(query)`
Search for packages using the system package manager.
- **Description**: Searches for packages matching the given `query` using the detected system package manager. Halts script execution if the package manager command fails.
- **Returns**: `Array` of `String` - An array containing the search results (package names and/or descriptions).
- **Arguments**:
- `query`: `String` - The search term.
```rhai
print("Searching for 'python' packages...");
let python_packages = os::package_search("python"); // Halts on package manager error
for pkg in python_packages {
print(`- ${pkg}`);
}
```
---
### `package_is_installed(package)`
Check if a package is installed using the system package manager.
- **Description**: Checks if the specified `package` is installed using the detected system package manager. Halts script execution if the package manager command itself fails (e.g., command not found), but does NOT halt if the package is simply not found.
- **Returns**: `Boolean` - `true` if the package is installed, `false` otherwise.
- **Arguments**:
- `package`: `String` - The name of the package to check (e.g., `"wget"`).
```rhai
let package_name = "wget";
if os::package_is_installed(package_name) { // Halts on package manager command error
print(`${package_name} is installed.`);
} else {
print(`${package_name} is not installed.`);
}
```
---
### `package_set_debug(debug)`
Set the debug mode for package management operations.
- **Description**: Enables or disables debug output for subsequent package management operations. This function does NOT halt on error and always returns the boolean value it was set to.
- **Returns**: `Boolean` - The boolean value that the debug flag was set to.
- **Arguments**:
- `debug`: `Boolean` - Set to `true` to enable debug output, `false` to disable.
```rhai
print("Enabling package debug output.");
os::package_set_debug(true);
// Subsequent package operations will print debug info
print("Disabling package debug output.");
os::package_set_debug(false);
```
---
### `package_platform()`
Get the current platform name for package management.
- **Description**: Returns the name of the operating system platform as detected by the package manager logic. This function does NOT halt on error; it returns `"Unknown"` if the platform cannot be determined.
- **Returns**: `String` - The platform name, one of `"Ubuntu"`, `"MacOS"`, or `"Unknown"`.
- **Arguments**: None.
```rhai
let platform = os::package_platform(); // Does not halt on error
print(`Detected package platform: ${platform}`);
```

237
docs/docs/sal/process.md Normal file
View File

@@ -0,0 +1,237 @@
---
title: "process"
sidebar_position: 3
hide_title: true
---
# Process Module
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(command)`
Runs a command or multiline script with arguments.
**Parameters:**
- `command` (string): The command to run (can be a single command or a multiline script)
**Returns:** The result of the command, including output and whether it succeeded.
**Example 1: Running a simple command**
```js
// Run a simple command
let result = run("ls -la");
// Check if the command was successful
if result.success {
print(`Command output: ${result.stdout}`);
} else {
print(`Command failed with error: ${result.stderr}`);
}
```
**Example 2: Running a multiline script**
```js
// Create a multiline script using backtick string literals
let setup_script = `
# Create directories
mkdir -p /tmp/test_project
cd /tmp/test_project
# Initialize git repository
git init
echo 'Initial content' > README.md
git add README.md
git config --local user.email 'test@example.com'
git config --local user.name 'Test User'
git commit -m 'Initial commit'
`;
// Execute the multiline script
let result = run(setup_script);
```
### `run_silent(command)`
Runs a command or multiline script with arguments silently (without displaying output).
**Parameters:**
- `command` (string): The command to run
**Returns:** The result of the command, without displaying the output.
**Example:**
```js
// Run a command silently
let result = run_silent("git pull");
// Check the exit code
if result.code == 0 {
print("Git pull successful");
} else {
print(`Git pull failed with code ${result.code}`);
}
```
### `new_run_options()`
Creates a new map with default run options.
**Returns:** A map with the following default options:
- `die` (boolean): `true` - Whether to throw an error if the command fails
- `silent` (boolean): `false` - Whether to suppress command output
- `async_exec` (boolean): `false` - Whether to run the command asynchronously
- `log` (boolean): `false` - Whether to log the command execution
**Example:**
```js
// Create run options
let options = new_run_options();
```
### `run_with_options(command, options)`
Runs a command with options specified in a map.
**Parameters:**
- `command` (string): The command to run
- `options` (map): A map of options created with `new_run_options()`
**Returns:** The result of the command with your custom settings applied.
**Example:**
```js
// Create and customize run options
let options = new_run_options();
options.die = false; // Don't throw an error if the command fails
options.silent = true; // Suppress command output
options.async_exec = false; // Run synchronously
options.log = true; // Log the command execution
// Run a command with options
let result = run_with_options("npm install", options);
```
## Working with Multiline Scripts
The Process module allows you to execute multiline scripts, which is particularly useful for complex operations that require multiple commands to be executed in sequence.
### Creating Multiline Scripts
Multiline scripts can be created using backtick (`) string literals in HeroScript:
```js
let my_script = `
# This is a multiline bash script
echo "Hello, World!"
mkdir -p /tmp/my_project
cd /tmp/my_project
touch example.txt
`;
```
## Process Management Functions
### `which(cmd)`
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 nothing if not found.
**Example:**
```js
// Check if a command exists
let git_path = which("git");
if git_path != () {
print(`Git is installed at: ${git_path}`);
} else {
print("Git is not installed");
}
```
### `kill(pattern)`
Kills processes matching a pattern.
**Parameters:**
- `pattern` (string): The pattern to match process names against
**Returns:** A message confirming the processes were killed.
**Example:**
```js
// Kill all processes with "node" in their name
kill("node");
```
### `process_list(pattern)`
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:** A list of processes matching your search.
**Example:**
```js
// List all processes
let all_processes = process_list("");
// List processes containing "node" in their name
let node_processes = process_list("node");
// Display process information
for process in node_processes {
print(`PID: ${process.pid}, Name: ${process.name}, Memory: ${process.memory}, CPU: ${process.cpu}`);
}
```
### `process_get(pattern)`
Gets a single process matching the pattern. Throws an error if zero or more than one process matches.
**Parameters:**
- `pattern` (string): The pattern to match process names against
**Returns:** Information about the matching process. This will only work if exactly one process matches.
**Example:**
```js
// Try to get a specific process
try {
let process = process_get("my_app");
print(`Found process: PID=${process.pid}, Name=${process.name}`);
} catch(err) {
print(`Error: ${err}`);
}
```

View File

@@ -0,0 +1,223 @@
# Process Module
The `process` module provides functions for running external commands and managing system processes using a builder pattern for command execution.
For running commands, you start with the `run()` function which returns a `CommandBuilder` object. You can then chain configuration methods like `silent()`, `ignore_error()`, and `log()` before finally calling the `do()` method to execute the command.
By default, command execution using the builder (`.do()`) will halt the script execution if the command itself fails (returns a non-zero exit code) or if there's an operating system error preventing the command from running. You can change this behavior with `ignore_error()`.
Other process management functions (`which`, `kill`, `process_list`, `process_get`) have specific error handling behaviors described below.
---
### `CommandResult`
An object returned by command execution functions (`.do()`) containing the result of the command.
- **Properties**:
- `stdout`: `String` - The standard output of the command.
- `stderr`: `String` - The standard error of the command.
- `success`: `Boolean` - `true` if the command exited with code 0, `false` otherwise.
- `code`: `Integer` - The exit code of the command.
```rhai
let result = run("echo hi").do();
print(`Success: ${result.success}, Output: ${result.stdout}`);
```
---
### `ProcessInfo`
An object found by process listing/getting functions (`process_list`, `process_get`) containing information about a running process.
- **Properties**:
- `pid`: `Integer` - The process ID.
- `name`: `String` - The name of the process executable.
- `memory`: `Integer` - The memory usage of the process (unit depends on the operating system, typically KB or bytes).
- `cpu`: `Float` - The CPU usage percentage (value and meaning may vary by operating system).
```rhai
let processes = process_list("my_service");
if (processes.len() > 0) {
let first_proc = processes[0];
print(`Process ${first_proc.name} (PID: ${first_proc.pid}) is running.`);
}
```
---
### `run(command)`
Start building a command execution.
- **Description**: Initializes a `CommandBuilder` for the given command string. This is the entry point to configure and run a process.
- **Returns**: `CommandBuilder` - A builder object for configuring the command.
- **Arguments**:
- `command`: `String` - The command string to execute. Can include arguments and be a simple multiline script.
```rhai
let cmd_builder = run("ls -l");
// Now you can chain methods like .silent(), .ignore_error(), .log()
```
---
### `CommandBuilder:silent()`
Configure the command to run silently.
- **Description**: Suppresses real-time standard output and standard error from being printed to the script's console during command execution. The output is still captured in the resulting `CommandResult`.
- **Returns**: `CommandBuilder` - Returns `self` for chaining.
- **Arguments**: None.
```rhai
print("Running silent command...");
run("echo This won\'t show directly").silent().do();
print("Silent command finished.");
```
---
### `CommandBuilder:ignore_error()`
Configure the command to ignore non-zero exit codes.
- **Description**: By default, the `do()` method halts script execution if the command returns a non-zero exit code. Calling `ignore_error()` prevents this. The `CommandResult` will still indicate `success: false` and contain the non-zero `code`, allowing the script to handle the command failure explicitly. OS errors preventing the command from running will still cause a halt.
- **Returns**: `CommandBuilder` - Returns `self` for chaining.
- **Arguments**: None.
```rhai
print("Running command that will fail but not halt...");
let result = run("exit 1").ignore_error().do(); // Will not halt
if (!result.success) {
print(`Command failed as expected with code: ${result.code}`);
}
```
---
### `CommandBuilder:log()`
Configure the command to log the execution details.
- **Description**: Enables logging of the command string before execution.
- **Returns**: `CommandBuilder` - Returns `self` for chaining.
- **Arguments**: None.
```rhai
print("Running command with logging...");
run("ls /tmp").log().do(); // Will print the "ls /tmp" command before running
print("Command finished.");
```
---
### `CommandBuilder:do()`
Execute the configured command.
- **Description**: Runs the command with the options set by the builder methods. Waits for the command to complete and returns the `CommandResult`. This method is the final step in the command execution builder chain. Halts based on the `ignore_error()` setting and OS errors.
- **Returns**: `CommandResult` - An object containing the output and status of the command.
- **Arguments**: None.
```rhai
print("Running command using builder...");
let command_result = run("pwd")
.log() // Log the command
.silent() // Don't print output live
.do(); // Execute and get result (halts on error by default)
print(`Command output: ${command_result.stdout}`);
// Example with multiple options
let fail_result = run("command_that_does_not_exist")
.ignore_error() // Don't halt on non-zero exit (though OS error might still halt)
.silent() // Don't print error live
.do();
if (!fail_result.success) {
print(`Failed command exited with code: ${fail_result.code} and stderr: ${fail_result.stderr}`);
}
```
---
### `which(cmd)`
Check if a command exists in the system PATH.
- **Description**: Searches the system's PATH environment variable for the executable `cmd`. This function does NOT halt if the command is not found; it returns an empty string.
- **Returns**: `String` - The full path to the command executable if found, otherwise an empty string (`""`).
- **Arguments**:
- `cmd`: `String` - The name of the command to search for (e.g., `"node"`).
```rhai
let node_path = which("node"); // Does not halt if node is not found
if (node_path != "") {
print(`Node executable found at: ${node_path}`);
} else {
print("Node executable not found in PATH.");
}
```
---
### `kill(pattern)`
Kill processes matching a pattern.
- **Description**: Terminates running processes whose names match the provided `pattern`. Uses platform-specific commands (like `pkill` or equivalent). Halts script execution on error interacting with the system process list or kill command.
- **Returns**: `String` - A success message indicating the kill attempt finished.
- **Arguments**:
- `pattern`: `String` - A pattern to match against process names (e.g., `"nginx"`).
```rhai
print("Attempting to kill processes matching 'my_service'...");
// Use with caution!
kill("my_service"); // Halts on OS error during kill attempt
print("Kill command sent.");
```
---
### `process_list(pattern)`
List processes matching a pattern (or all if pattern is empty).
- **Description**: Lists information about running processes whose names match the provided `pattern`. If `pattern` is an empty string `""`, lists all processes. Halts script execution on error interacting with the system process list. Returns an empty array if no processes match the pattern.
- **Returns**: `Array` of `ProcessInfo` - An array of objects, each containing `pid` (Integer), `name` (String), `memory` (Integer), and `cpu` (Float).
- **Arguments**:
- `pattern`: `String` - A pattern to match against process names, or `""` for all processes.
```rhai
print("Listing processes matching 'bash'...");
let bash_processes = process_list("bash"); // Halts on OS error
if (bash_processes.len() > 0) {
print("Found bash processes:");
for proc in bash_processes {
print(`- PID: ${proc.pid}, Name: ${proc.name}, CPU: ${proc.cpu}%, Memory: ${proc.memory}`);
}
} else {
print("No bash processes found.");
}
```
---
### `process_get(pattern)`
Get a single process matching the pattern (error if 0 or more than 1 match).
- **Description**: Finds exactly one running process whose name matches the provided `pattern`. Halts script execution if zero or more than one process matches the pattern, or on error interacting with the system process list.
- **Returns**: `ProcessInfo` - An object containing `pid` (Integer), `name` (String), `memory` (Integer), and `cpu` (Float).
- **Arguments**:
- `pattern`: `String` - A pattern to match against process names, expected to match exactly one process.
```rhai
let expected_service_name = "my_critical_service";
print(`Getting process info for '${expected_service_name}'...`);
// This will halt if the service isn't running, or if multiple services have this name
let service_proc_info = process_get(expected_service_name);
print(`Found process: PID ${service_proc_info.pid}, Name: ${service_proc_info.name}`);
```

154
docs/docs/sal/rfs.md Normal file
View File

@@ -0,0 +1,154 @@
# RFS (Remote File System)
The RFS module provides a Rust wrapper for the RFS tool, which allows mounting remote filesystems locally and managing filesystem layers.
## Overview
RFS (Remote File System) is a tool that enables mounting various types of remote filesystems locally, as well as creating and managing filesystem layers. The SAL library provides a Rust wrapper for RFS with a fluent builder API, making it easy to use in your applications.
## Features
- Mount remote filesystems locally (SSH, S3, WebDAV, etc.)
- List mounted filesystems
- Unmount filesystems
- Pack directories into filesystem layers
- Unpack filesystem layers
- List contents of filesystem layers
- Verify filesystem layers
## Usage in Rust
### Mounting a Filesystem
```rust
use sal::virt::rfs::{RfsBuilder, MountType};
// Create a new RFS builder
let mount = RfsBuilder::new("user@example.com:/remote/path", "/local/mount/point", MountType::SSH)
.with_option("port", "2222")
.with_option("identity_file", "/path/to/key")
.with_debug(true)
.mount()?;
println!("Mounted filesystem with ID: {}", mount.id);
```
### Listing Mounts
```rust
use sal::virt::rfs::list_mounts;
// List all mounts
let mounts = list_mounts()?;
for mount in mounts {
println!("Mount ID: {}, Source: {}, Target: {}", mount.id, mount.source, mount.target);
}
```
### Unmounting a Filesystem
```rust
use sal::virt::rfs::unmount;
// Unmount a filesystem
unmount("/local/mount/point")?;
```
### Packing a Directory
```rust
use sal::virt::rfs::{PackBuilder, StoreSpec};
// Create store specifications
let store_spec = StoreSpec::new("file")
.with_option("path", "/path/to/store");
// Pack a directory with builder pattern
let result = PackBuilder::new("/path/to/directory", "output.fl")
.with_store_spec(store_spec)
.with_debug(true)
.pack()?;
```
### Unpacking a Filesystem Layer
```rust
use sal::virt::rfs::unpack;
// Unpack a filesystem layer
unpack("input.fl", "/path/to/unpack")?;
```
## Usage in Rhai Scripts
### Mounting a Filesystem
```rhai
// Create a map for mount options
let options = #{
"port": "22",
"identity_file": "/path/to/key",
"readonly": "true"
};
// Mount the directory
let mount = rfs_mount("user@example.com:/remote/path", "/local/mount/point", "ssh", options);
print(`Mounted ${mount.source} to ${mount.target} with ID: ${mount.id}`);
```
### Listing Mounts
```rhai
// List all mounts
let mounts = rfs_list_mounts();
print(`Number of mounts: ${mounts.len()}`);
for mount in mounts {
print(`Mount ID: ${mount.id}, Source: ${mount.source}, Target: ${mount.target}`);
}
```
### Unmounting a Filesystem
```rhai
// Unmount the directory
rfs_unmount("/local/mount/point");
```
### Packing a Directory
```rhai
// Pack the directory
// Store specs format: "file:path=/path/to/store,s3:bucket=my-bucket"
rfs_pack("/path/to/directory", "output.fl", "file:path=/path/to/store");
```
### Unpacking a Filesystem Layer
```rhai
// Unpack the filesystem layer
rfs_unpack("output.fl", "/path/to/unpack");
```
## Mount Types
The RFS module supports various mount types:
- **Local**: Mount a local directory
- **SSH**: Mount a remote directory via SSH
- **S3**: Mount an S3 bucket
- **WebDAV**: Mount a WebDAV server
## Store Specifications
When packing a directory into a filesystem layer, you can specify one or more stores to use. Each store has a type and options:
- **File**: Store files on the local filesystem
- Options: `path` (path to the store)
- **S3**: Store files in an S3 bucket
- Options: `bucket` (bucket name), `region` (AWS region), `access_key`, `secret_key`
## Examples
See the [RFS example script](../../rhaiexamples/rfs_example.rhai) for more examples of how to use the RFS module in Rhai scripts.

237
docs/docs/sal/text.md Normal file
View File

@@ -0,0 +1,237 @@
# Text Manipulation Tools
The SAL text module provides powerful text manipulation capabilities that can be used from Rhai scripts. These include text replacement (with regex support), template rendering, string normalization, and text formatting utilities.
## Table of Contents
- [Text Replacement](#text-replacement)
- [Template Rendering](#template-rendering)
- [String Normalization](#string-normalization)
- [Text Formatting](#text-formatting)
## Text Replacement
The text replacement tools allow you to perform simple or complex text replacements, with support for regular expressions, case-insensitive matching, and file operations.
### Basic Usage
```rhai
// Create a new text replacer
let replacer = text_replacer_new()
.pattern("foo") // Set the pattern to search for
.replacement("bar") // Set the replacement text
.build(); // Build the replacer
// Apply the replacer to a string
let result = replacer.replace("foo bar foo");
// Result: "bar bar bar"
```
### Advanced Features
#### Regular Expressions
```rhai
// Create a replacer with regex support
let replacer = text_replacer_new()
.pattern("\\bfoo\\b") // Use regex pattern (word boundary)
.replacement("bar")
.regex(true) // Enable regex mode
.build();
// Apply the replacer to a string
let result = replacer.replace("foo foobar");
// Result: "bar foobar" (only replaces whole "foo" words)
```
#### Case-Insensitive Matching
```rhai
// Create a replacer with case-insensitive matching
let replacer = text_replacer_new()
.pattern("foo")
.replacement("bar")
.regex(true)
.case_insensitive(true) // Enable case-insensitive matching
.build();
// Apply the replacer to a string
let result = replacer.replace("FOO foo Foo");
// Result: "bar bar bar"
```
#### Multiple Replacements
```rhai
// Chain multiple replacements
let replacer = text_replacer_new()
.pattern("foo")
.replacement("bar")
.and() // Add another replacement operation
.pattern("baz")
.replacement("qux")
.build();
// Apply the replacer to a string
let result = replacer.replace("foo baz");
// Result: "bar qux"
```
#### File Operations
```rhai
// Create a replacer
let replacer = text_replacer_new()
.pattern("foo")
.replacement("bar")
.build();
// Replace in a file and get the result as a string
let result = replacer.replace_file("input.txt");
// Replace in a file and write back to the same file
replacer.replace_file_in_place("input.txt");
// Replace in a file and write to a new file
replacer.replace_file_to("input.txt", "output.txt");
```
## Template Rendering
The template rendering tools allow you to create and render templates with variables, using the powerful Tera template engine.
### Basic Usage
```rhai
// Create a template builder with a template file
let template = template_builder_open("template.txt")
.add_var("name", "John") // Add a string variable
.add_var("age", 30) // Add a numeric variable
.add_var("items", ["a", "b", "c"]); // Add an array variable
// Render the template
let result = template.render();
// Render to a file
template.render_to_file("output.txt");
```
### Template Variables
You can add variables of various types:
```rhai
let template = template_builder_open("template.txt")
.add_var("name", "John") // String
.add_var("age", 30) // Integer
.add_var("height", 1.85) // Float
.add_var("is_active", true) // Boolean
.add_var("items", ["a", "b", "c"]); // Array
```
### Using Map for Variables
```rhai
// Create a map of variables
let vars = #{
name: "Alice",
place: "Wonderland"
};
// Add all variables from the map
let template = template_builder_open("template.txt")
.add_vars(vars);
```
### Template Syntax
The template engine uses Tera, which supports:
- Variable interpolation: `{{ variable }}`
- Conditionals: `{% if condition %}...{% endif %}`
- Loops: `{% for item in items %}...{% endfor %}`
- Filters: `{{ variable | filter }}`
Example template:
```
Hello, {{ name }}!
{% if show_greeting %}
Welcome to {{ place }}.
{% endif %}
Your items:
{% for item in items %}
- {{ item }}{% if not loop.last %}{% endif %}
{% endfor %}
```
## String Normalization
The string normalization tools help convert strings to consistent formats for use as file names or paths.
### name_fix
Converts a string to a safe, normalized name by:
- Converting to lowercase
- Replacing spaces and special characters with underscores
- Removing non-alphanumeric characters
```rhai
let fixed_name = name_fix("Hello World!");
// Result: "hello_world"
let fixed_name = name_fix("File-Name.txt");
// Result: "file_name.txt"
```
### path_fix
Similar to name_fix, but preserves path separators:
```rhai
let fixed_path = path_fix("/path/to/Hello World!");
// Result: "/path/to/hello_world"
let fixed_path = path_fix("./relative/path/to/DOCUMENT-123.pdf");
// Result: "./relative/path/to/document_123.pdf"
```
## Text Formatting
Tools to help with text formatting and indentation.
### dedent
Removes common leading whitespace from multi-line strings:
```rhai
let indented_text = " line 1
line 2
line 3";
let dedented = dedent(indented_text);
// Result: "line 1
// line 2
// line 3"
```
### prefix
Adds a prefix to every line in a multi-line string:
```rhai
let text = "line 1
line 2
line 3";
let prefixed = prefix(text, " ");
// Result: " line 1
// line 2
// line 3"
```
## Examples
See the [text_tools.rhai](https://github.com/ourworld-tf/herocode/blob/main/sal/src/rhaiexamples/text_tools.rhai) example script for more detailed examples of using these text manipulation tools.