2025-12-11 21:50:42 +00:00
<!DOCTYPE HTML>
< html lang = "en" class = "ayu sidebar-visible" dir = "ltr" >
< head >
<!-- Book generated using mdBook -->
< meta charset = "UTF-8" >
< title > Workflow - Provisioning Platform Documentation< / title >
<!-- Custom HTML head -->
2026-01-14 01:56:30 +00:00
< meta name = "description" content = "Complete documentation for the Provisioning Platform - Infrastructure automation with Nushell, Nickel, and Rust" >
2025-12-11 21:50:42 +00:00
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< meta name = "theme-color" content = "#ffffff" >
< link rel = "icon" href = "../favicon.svg" >
< link rel = "shortcut icon" href = "../favicon.png" >
< link rel = "stylesheet" href = "../css/variables.css" >
< link rel = "stylesheet" href = "../css/general.css" >
< link rel = "stylesheet" href = "../css/chrome.css" >
< link rel = "stylesheet" href = "../css/print.css" media = "print" >
<!-- Fonts -->
< link rel = "stylesheet" href = "../FontAwesome/css/font-awesome.css" >
< link rel = "stylesheet" href = "../fonts/fonts.css" >
<!-- Highlight.js Stylesheets -->
< link rel = "stylesheet" id = "highlight-css" href = "../highlight.css" >
< link rel = "stylesheet" id = "tomorrow-night-css" href = "../tomorrow-night.css" >
< link rel = "stylesheet" id = "ayu-highlight-css" href = "../ayu-highlight.css" >
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
< script >
const path_to_root = "../";
const default_light_theme = "ayu";
const default_dark_theme = "navy";
< / script >
<!-- Start loading toc.js asap -->
< script src = "../toc.js" > < / script >
< / head >
< body >
< div id = "mdbook-help-container" >
< div id = "mdbook-help-popup" >
< h2 class = "mdbook-help-title" > Keyboard shortcuts< / h2 >
< div >
< p > Press < kbd > ←< / kbd > or < kbd > →< / kbd > to navigate between chapters< / p >
< p > Press < kbd > S< / kbd > or < kbd > /< / kbd > to search in the book< / p >
< p > Press < kbd > ?< / kbd > to show this help< / p >
< p > Press < kbd > Esc< / kbd > to hide this help< / p >
< / div >
< / div >
< / div >
< div id = "body-container" >
<!-- Work around some values being stored in localStorage wrapped in quotes -->
< script >
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') & & theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') & & sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
< / script >
<!-- Set the theme before any content is loaded, prevents flash -->
< script >
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('ayu')
html.classList.add(theme);
html.classList.add("js");
< / script >
< input type = "checkbox" id = "sidebar-toggle-anchor" class = "hidden" >
<!-- Hide / unhide sidebar before it is displayed -->
< script >
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
< / script >
< nav id = "sidebar" class = "sidebar" aria-label = "Table of contents" >
<!-- populated by js -->
< mdbook-sidebar-scrollbox class = "sidebar-scrollbox" > < / mdbook-sidebar-scrollbox >
< noscript >
< iframe class = "sidebar-iframe-outer" src = "../toc.html" > < / iframe >
< / noscript >
< div id = "sidebar-resize-handle" class = "sidebar-resize-handle" >
< div class = "sidebar-resize-indicator" > < / div >
< / div >
< / nav >
< div id = "page-wrapper" class = "page-wrapper" >
< div class = "page" >
< div id = "menu-bar-hover-placeholder" > < / div >
< div id = "menu-bar" class = "menu-bar sticky" >
< div class = "left-buttons" >
< label id = "sidebar-toggle" class = "icon-button" for = "sidebar-toggle-anchor" title = "Toggle Table of Contents" aria-label = "Toggle Table of Contents" aria-controls = "sidebar" >
< i class = "fa fa-bars" > < / i >
< / label >
< button id = "theme-toggle" class = "icon-button" type = "button" title = "Change theme" aria-label = "Change theme" aria-haspopup = "true" aria-expanded = "false" aria-controls = "theme-list" >
< i class = "fa fa-paint-brush" > < / i >
< / button >
< ul id = "theme-list" class = "theme-popup" aria-label = "Themes" role = "menu" >
< li role = "none" > < button role = "menuitem" class = "theme" id = "default_theme" > Auto< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "light" > Light< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "rust" > Rust< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "coal" > Coal< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "navy" > Navy< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "ayu" > Ayu< / button > < / li >
< / ul >
< button id = "search-toggle" class = "icon-button" type = "button" title = "Search (`/`)" aria-label = "Toggle Searchbar" aria-expanded = "false" aria-keyshortcuts = "/ s" aria-controls = "searchbar" >
< i class = "fa fa-search" > < / i >
< / button >
< / div >
< h1 class = "menu-title" > Provisioning Platform Documentation< / h1 >
< div class = "right-buttons" >
< a href = "../print.html" title = "Print this book" aria-label = "Print this book" >
< i id = "print-button" class = "fa fa-print" > < / i >
< / a >
< a href = "https://github.com/provisioning/provisioning-platform" title = "Git repository" aria-label = "Git repository" >
< i id = "git-repository-button" class = "fa fa-github" > < / i >
< / a >
< a href = "https://github.com/provisioning/provisioning-platform/edit/main/provisioning/docs/src/development/workflow.md" title = "Suggest an edit" aria-label = "Suggest an edit" >
< i id = "git-edit-button" class = "fa fa-edit" > < / i >
< / a >
< / div >
< / div >
< div id = "search-wrapper" class = "hidden" >
< form id = "searchbar-outer" class = "searchbar-outer" >
< input type = "search" id = "searchbar" name = "searchbar" placeholder = "Search this book ..." aria-controls = "searchresults-outer" aria-describedby = "searchresults-header" >
< / form >
< div id = "searchresults-outer" class = "searchresults-outer hidden" >
< div id = "searchresults-header" class = "searchresults-header" > < / div >
< ul id = "searchresults" >
< / ul >
< / div >
< / div >
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
< script >
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
< / script >
< div id = "content" class = "content" >
< main >
< h1 id = "development-workflow-guide" > < a class = "header" href = "#development-workflow-guide" > Development Workflow Guide< / a > < / h1 >
2026-01-14 01:56:30 +00:00
< p > This document outlines the recommended development workflows, coding practices, testing strategies, and debugging techniques for the provisioning
project.< / p >
2025-12-11 21:50:42 +00:00
< h2 id = "table-of-contents" > < a class = "header" href = "#table-of-contents" > Table of Contents< / a > < / h2 >
< ol >
< li > < a href = "#overview" > Overview< / a > < / li >
< li > < a href = "#development-setup" > Development Setup< / a > < / li >
< li > < a href = "#daily-development-workflow" > Daily Development Workflow< / a > < / li >
< li > < a href = "#code-organization" > Code Organization< / a > < / li >
< li > < a href = "#testing-strategies" > Testing Strategies< / a > < / li >
< li > < a href = "#debugging-techniques" > Debugging Techniques< / a > < / li >
< li > < a href = "#integration-workflows" > Integration Workflows< / a > < / li >
< li > < a href = "#collaboration-guidelines" > Collaboration Guidelines< / a > < / li >
< li > < a href = "#quality-assurance" > Quality Assurance< / a > < / li >
< li > < a href = "#best-practices" > Best Practices< / a > < / li >
< / ol >
< h2 id = "overview" > < a class = "header" href = "#overview" > Overview< / a > < / h2 >
2026-01-14 01:56:30 +00:00
< p > The provisioning project employs a multi-language, multi-component architecture requiring specific development workflows to maintain consistency,
quality, and efficiency.< / p >
2025-12-11 21:50:42 +00:00
< p > < strong > Key Technologies< / strong > :< / p >
< ul >
< li > < strong > Nushell< / strong > : Primary scripting and automation language< / li >
< li > < strong > Rust< / strong > : High-performance system components< / li >
< li > < strong > KCL< / strong > : Configuration language and schemas< / li >
< li > < strong > TOML< / strong > : Configuration files< / li >
< li > < strong > Jinja2< / strong > : Template engine< / li >
< / ul >
< p > < strong > Development Principles< / strong > :< / p >
< ul >
< li > < strong > Configuration-Driven< / strong > : Never hardcode, always configure< / li >
< li > < strong > Hybrid Architecture< / strong > : Rust for performance, Nushell for flexibility< / li >
< li > < strong > Test-First< / strong > : Comprehensive testing at all levels< / li >
< li > < strong > Documentation-Driven< / strong > : Code and APIs are self-documenting< / li >
< / ul >
< h2 id = "development-setup" > < a class = "header" href = "#development-setup" > Development Setup< / a > < / h2 >
< h3 id = "initial-environment-setup" > < a class = "header" href = "#initial-environment-setup" > Initial Environment Setup< / a > < / h3 >
< p > < strong > 1. Clone and Navigate< / strong > :< / p >
< pre > < code class = "language-bash" > # Clone repository
git clone https://github.com/company/provisioning-system.git
cd provisioning-system
# Navigate to workspace
cd workspace/tools
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > 2. Initialize Workspace< / strong > :< / p >
< pre > < code class = "language-bash" > # Initialize development workspace
2025-12-11 21:50:42 +00:00
nu workspace.nu init --user-name $USER --infra-name dev-env
# Check workspace health
nu workspace.nu health --detailed --fix-issues
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > 3. Configure Development Environment< / strong > :< / p >
< pre > < code class = "language-bash" > # Create user configuration
2025-12-11 21:50:42 +00:00
cp workspace/config/local-overrides.toml.example workspace/config/$USER.toml
# Edit configuration for development
$EDITOR workspace/config/$USER.toml
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > 4. Set Up Build System< / strong > :< / p >
< pre > < code class = "language-bash" > # Navigate to build tools
2025-12-11 21:50:42 +00:00
cd src/tools
# Check build prerequisites
make info
# Perform initial build
make dev-build
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "tool-installation" > < a class = "header" href = "#tool-installation" > Tool Installation< / a > < / h3 >
< p > < strong > Required Tools< / strong > :< / p >
< pre > < code class = "language-bash" > # Install Nushell
2025-12-11 21:50:42 +00:00
cargo install nu
2026-01-12 04:42:18 +00:00
# Install Nickel
cargo install nickel
2025-12-11 21:50:42 +00:00
# Install additional tools
cargo install cross # Cross-compilation
cargo install cargo-audit # Security auditing
cargo install cargo-watch # File watching
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Optional Development Tools< / strong > :< / p >
< pre > < code class = "language-bash" > # Install development enhancers
2025-12-11 21:50:42 +00:00
cargo install nu_plugin_tera # Template plugin
cargo install sops # Secrets management
brew install k9s # Kubernetes management
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "ide-configuration" > < a class = "header" href = "#ide-configuration" > IDE Configuration< / a > < / h3 >
< p > < strong > VS Code Setup< / strong > (< code > .vscode/settings.json< / code > ):< / p >
< pre > < code class = "language-json" > {
2025-12-11 21:50:42 +00:00
"files.associations": {
"*.nu": "shellscript",
2026-01-12 04:42:18 +00:00
"*.ncl": "nickel",
2025-12-11 21:50:42 +00:00
"*.toml": "toml"
},
"nushell.shellPath": "/usr/local/bin/nu",
"rust-analyzer.cargo.features": "all",
"editor.formatOnSave": true,
"editor.rulers": [100],
"files.trimTrailingWhitespace": true
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Recommended Extensions< / strong > :< / p >
< ul >
< li > Nushell Language Support< / li >
< li > Rust Analyzer< / li >
< li > Nickel Language Support< / li >
< li > TOML Language Support< / li >
< li > Better TOML< / li >
< / ul >
< h2 id = "daily-development-workflow" > < a class = "header" href = "#daily-development-workflow" > Daily Development Workflow< / a > < / h2 >
< h3 id = "morning-routine" > < a class = "header" href = "#morning-routine" > Morning Routine< / a > < / h3 >
< p > < strong > 1. Sync and Update< / strong > :< / p >
< pre > < code class = "language-bash" > # Sync with upstream
2025-12-11 21:50:42 +00:00
git pull origin main
# Update workspace
cd workspace/tools
nu workspace.nu health --fix-issues
# Check for updates
nu workspace.nu status --detailed
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > 2. Review Current State< / strong > :< / p >
< pre > < code class = "language-bash" > # Check current infrastructure
2025-12-11 21:50:42 +00:00
provisioning show servers
provisioning show settings
# Review workspace status
nu workspace.nu status
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "development-cycle" > < a class = "header" href = "#development-cycle" > Development Cycle< / a > < / h3 >
< p > < strong > 1. Feature Development< / strong > :< / p >
< pre > < code class = "language-bash" > # Create feature branch
2025-12-11 21:50:42 +00:00
git checkout -b feature/new-provider-support
# Start development environment
cd workspace/tools
nu workspace.nu init --workspace-type development
# Begin development
$EDITOR workspace/extensions/providers/new-provider/nulib/provider.nu
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > 2. Incremental Testing< / strong > :< / p >
< pre > < code class = "language-bash" > # Test syntax during development
2025-12-11 21:50:42 +00:00
nu --check workspace/extensions/providers/new-provider/nulib/provider.nu
# Run unit tests
nu workspace/extensions/providers/new-provider/tests/unit/basic-test.nu
# Integration testing
nu workspace.nu tools test-extension providers/new-provider
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > 3. Build and Validate< / strong > :< / p >
< pre > < code class = "language-bash" > # Quick development build
2025-12-11 21:50:42 +00:00
cd src/tools
make dev-build
# Validate changes
make validate-all
# Test distribution
make test-dist
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "testing-during-development" > < a class = "header" href = "#testing-during-development" > Testing During Development< / a > < / h3 >
< p > < strong > Unit Testing< / strong > :< / p >
< pre > < code class = "language-nushell" > # Add test examples to functions
2025-12-11 21:50:42 +00:00
def create-server [name: string] -> record {
# @test: "test-server" -> {name: "test-server", status: "created"}
# Implementation here
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Integration Testing< / strong > :< / p >
< pre > < code class = "language-bash" > # Test with real infrastructure
2025-12-11 21:50:42 +00:00
nu workspace/extensions/providers/new-provider/nulib/provider.nu \
create-server test-server --dry-run
# Test with workspace isolation
PROVISIONING_WORKSPACE_USER=$USER provisioning server create test-server --check
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "end-of-day-routine" > < a class = "header" href = "#end-of-day-routine" > End-of-Day Routine< / a > < / h3 >
< p > < strong > 1. Commit Progress< / strong > :< / p >
< pre > < code class = "language-bash" > # Stage changes
2025-12-11 21:50:42 +00:00
git add .
# Commit with descriptive message
git commit -m "feat(provider): add new cloud provider support
- Implement basic server creation
- Add configuration schema
- Include unit tests
- Update documentation"
# Push to feature branch
git push origin feature/new-provider-support
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > 2. Workspace Maintenance< / strong > :< / p >
< pre > < code class = "language-bash" > # Clean up development data
2025-12-11 21:50:42 +00:00
nu workspace.nu cleanup --type cache --age 1d
# Backup current state
nu workspace.nu backup --auto-name --components config,extensions
# Check workspace health
nu workspace.nu health
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "code-organization" > < a class = "header" href = "#code-organization" > Code Organization< / a > < / h2 >
< h3 id = "nushell-code-structure" > < a class = "header" href = "#nushell-code-structure" > Nushell Code Structure< / a > < / h3 >
< p > < strong > File Organization< / strong > :< / p >
< pre > < code class = "language-plaintext" > Extension Structure:
2025-12-11 21:50:42 +00:00
├── nulib/
│ ├── main.nu # Main entry point
│ ├── core/ # Core functionality
│ │ ├── api.nu # API interactions
│ │ ├── config.nu # Configuration handling
│ │ └── utils.nu # Utility functions
│ ├── commands/ # User commands
│ │ ├── create.nu # Create operations
│ │ ├── delete.nu # Delete operations
│ │ └── list.nu # List operations
│ └── tests/ # Test files
│ ├── unit/ # Unit tests
│ └── integration/ # Integration tests
└── templates/ # Template files
├── config.j2 # Configuration templates
└── manifest.j2 # Manifest templates
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Function Naming Conventions< / strong > :< / p >
< pre > < code class = "language-nushell" > # Use kebab-case for commands
2025-12-11 21:50:42 +00:00
def create-server [name: string] -> record { ... }
def validate-config [config: record] -> bool { ... }
# Use snake_case for internal functions
def get_api_client [] -> record { ... }
def parse_config_file [path: string] -> record { ... }
# Use descriptive prefixes
def check-server-status [server: string] -> string { ... }
def get-server-info [server: string] -> record { ... }
def list-available-zones [] -> list< string> { ... }
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Error Handling Pattern< / strong > :< / p >
< pre > < code class = "language-nushell" > def create-server [
2025-12-11 21:50:42 +00:00
name: string
--dry-run: bool = false
] -> record {
# 1. Validate inputs
if ($name | str length) == 0 {
error make {
msg: "Server name cannot be empty"
label: {
text: "empty name provided"
span: (metadata $name).span
}
}
}
# 2. Check prerequisites
let config = try {
get-provider-config
} catch {
error make {msg: "Failed to load provider configuration"}
}
# 3. Perform operation
if $dry_run {
return {action: "create", server: $name, status: "dry-run"}
}
# 4. Return result
{server: $name, status: "created", id: (generate-id)}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "rust-code-structure" > < a class = "header" href = "#rust-code-structure" > Rust Code Structure< / a > < / h3 >
< p > < strong > Project Organization< / strong > :< / p >
< pre > < code class = "language-plaintext" > src/
2025-12-11 21:50:42 +00:00
├── lib.rs # Library root
├── main.rs # Binary entry point
├── config/ # Configuration handling
│ ├── mod.rs
│ ├── loader.rs # Config loading
│ └── validation.rs # Config validation
├── api/ # HTTP API
│ ├── mod.rs
│ ├── handlers.rs # Request handlers
│ └── middleware.rs # Middleware components
└── orchestrator/ # Orchestration logic
├── mod.rs
├── workflow.rs # Workflow management
└── task_queue.rs # Task queue management
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Error Handling< / strong > :< / p >
< pre > < code class = "language-rust" > use anyhow::{Context, Result};
2025-12-11 21:50:42 +00:00
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ProvisioningError {
#[error("Configuration error: {message}")]
Config { message: String },
#[error("Network error: {source}")]
Network {
#[from]
source: reqwest::Error,
},
#[error("Validation failed: {field}")]
Validation { field: String },
}
pub fn create_server(name: & str) -> Result< ServerInfo> {
let config = load_config()
.context("Failed to load configuration")?;
validate_server_name(name)
.context("Server name validation failed")?;
let server = provision_server(name, & config)
.context("Failed to provision server")?;
Ok(server)
2026-01-12 04:42:18 +00:00
}< / code > < / pre >
< h3 id = "nickel-schema-organization" > < a class = "header" href = "#nickel-schema-organization" > Nickel Schema Organization< / a > < / h3 >
< p > < strong > Schema Structure< / strong > :< / p >
< pre > < code class = "language-nickel" > # Base schema definitions
let ServerConfig = {
name | string,
plan | string,
zone | string,
tags | { } | default = {},
} in
ServerConfig
2025-12-11 21:50:42 +00:00
# Provider-specific extensions
2026-01-12 04:42:18 +00:00
let UpCloudServerConfig = {
template | string | default = "Ubuntu Server 22.04 LTS (Jammy Jellyfish)",
storage | number | default = 25,
} in
UpCloudServerConfig
2025-12-11 21:50:42 +00:00
# Composition schemas
2026-01-12 04:42:18 +00:00
let InfrastructureConfig = {
servers | array,
networks | array | default = [],
load_balancers | array | default = [],
} in
InfrastructureConfig
< / code > < / pre >
< h2 id = "testing-strategies" > < a class = "header" href = "#testing-strategies" > Testing Strategies< / a > < / h2 >
< h3 id = "test-driven-development" > < a class = "header" href = "#test-driven-development" > Test-Driven Development< / a > < / h3 >
< p > < strong > TDD Workflow< / strong > :< / p >
< ol >
< li > < strong > Write Test First< / strong > : Define expected behavior< / li >
< li > < strong > Run Test (Fail)< / strong > : Confirm test fails as expected< / li >
< li > < strong > Write Code< / strong > : Implement minimal code to pass< / li >
< li > < strong > Run Test (Pass)< / strong > : Confirm test now passes< / li >
< li > < strong > Refactor< / strong > : Improve code while keeping tests green< / li >
< / ol >
< h3 id = "nushell-testing" > < a class = "header" href = "#nushell-testing" > Nushell Testing< / a > < / h3 >
< p > < strong > Unit Test Pattern< / strong > :< / p >
< pre > < code class = "language-nushell" > # Function with embedded test
2025-12-11 21:50:42 +00:00
def validate-server-name [name: string] -> bool {
# @test: "valid-name" -> true
# @test: "" -> false
# @test: "name-with-spaces" -> false
if ($name | str length) == 0 {
return false
}
if ($name | str contains " ") {
return false
}
true
}
# Separate test file
# tests/unit/server-validation-test.nu
def test_validate_server_name [] {
# Valid cases
assert (validate-server-name "valid-name")
assert (validate-server-name "server123")
# Invalid cases
assert not (validate-server-name "")
assert not (validate-server-name "name with spaces")
assert not (validate-server-name "name@with!special")
print "✅ validate-server-name tests passed"
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Integration Test Pattern< / strong > :< / p >
< pre > < code class = "language-nushell" > # tests/integration/server-lifecycle-test.nu
2025-12-11 21:50:42 +00:00
def test_complete_server_lifecycle [] {
# Setup
let test_server = "test-server-" + (date now | format date "%Y%m%d%H%M%S")
try {
# Test creation
let create_result = (create-server $test_server --dry-run)
assert ($create_result.status == "dry-run")
# Test validation
let validate_result = (validate-server-config $test_server)
assert $validate_result
print $"✅ Server lifecycle test passed for ($test_server)"
} catch { |e|
print $"❌ Server lifecycle test failed: ($e.msg)"
exit 1
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "rust-testing" > < a class = "header" href = "#rust-testing" > Rust Testing< / a > < / h3 >
< p > < strong > Unit Testing< / strong > :< / p >
< pre > < code class = "language-rust" > #[cfg(test)]
2025-12-11 21:50:42 +00:00
mod tests {
use super::*;
use tokio_test;
#[test]
fn test_validate_server_name() {
assert!(validate_server_name("valid-name"));
assert!(validate_server_name("server123"));
assert!(!validate_server_name(""));
assert!(!validate_server_name("name with spaces"));
assert!(!validate_server_name("name@special"));
}
#[tokio::test]
async fn test_server_creation() {
let config = test_config();
let result = create_server("test-server", & config).await;
assert!(result.is_ok());
let server = result.unwrap();
assert_eq!(server.name, "test-server");
assert_eq!(server.status, "created");
}
2026-01-12 04:42:18 +00:00
}< / code > < / pre >
< p > < strong > Integration Testing< / strong > :< / p >
< pre > < code class = "language-rust" > #[cfg(test)]
2025-12-11 21:50:42 +00:00
mod integration_tests {
use super::*;
use testcontainers::*;
#[tokio::test]
async fn test_full_workflow() {
// Setup test environment
let docker = clients::Cli::default();
let postgres = docker.run(images::postgres::Postgres::default());
let config = TestConfig {
database_url: format!("postgresql://localhost:{}/test",
postgres.get_host_port_ipv4(5432))
};
// Test complete workflow
let workflow = create_workflow(& config).await.unwrap();
let result = execute_workflow(workflow).await.unwrap();
assert_eq!(result.status, WorkflowStatus::Completed);
}
2026-01-12 04:42:18 +00:00
}< / code > < / pre >
< h3 id = "nickel-testing" > < a class = "header" href = "#nickel-testing" > Nickel Testing< / a > < / h3 >
< p > < strong > Schema Validation Testing< / strong > :< / p >
< pre > < code class = "language-bash" > # Test Nickel schemas
nickel check schemas/
2025-12-11 21:50:42 +00:00
# Validate specific schemas
2026-01-12 04:42:18 +00:00
nickel typecheck schemas/server.ncl
2025-12-11 21:50:42 +00:00
# Test with examples
2026-01-12 04:42:18 +00:00
nickel eval schemas/server.ncl
< / code > < / pre >
< h3 id = "test-automation" > < a class = "header" href = "#test-automation" > Test Automation< / a > < / h3 >
< p > < strong > Continuous Testing< / strong > :< / p >
< pre > < code class = "language-bash" > # Watch for changes and run tests
2025-12-11 21:50:42 +00:00
cargo watch -x test -x check
# Watch Nushell files
find . -name "*.nu" | entr -r nu tests/run-all-tests.nu
# Automated testing in workspace
nu workspace.nu tools test-all --watch
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "debugging-techniques" > < a class = "header" href = "#debugging-techniques" > Debugging Techniques< / a > < / h2 >
< h3 id = "debug-configuration" > < a class = "header" href = "#debug-configuration" > Debug Configuration< / a > < / h3 >
< p > < strong > Enable Debug Mode< / strong > :< / p >
< pre > < code class = "language-bash" > # Environment variables
2025-12-11 21:50:42 +00:00
export PROVISIONING_DEBUG=true
export PROVISIONING_LOG_LEVEL=debug
export RUST_LOG=debug
export RUST_BACKTRACE=1
# Workspace debug
export PROVISIONING_WORKSPACE_USER=$USER
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "nushell-debugging" > < a class = "header" href = "#nushell-debugging" > Nushell Debugging< / a > < / h3 >
< p > < strong > Debug Techniques< / strong > :< / p >
< pre > < code class = "language-nushell" > # Debug prints
2025-12-11 21:50:42 +00:00
def debug-server-creation [name: string] {
print $"🐛 Creating server: ($name)"
let config = get-provider-config
print $"🐛 Config loaded: ($config | to json)"
let result = try {
create-server-api $name $config
} catch { |e|
print $"🐛 API call failed: ($e.msg)"
$e
}
print $"🐛 Result: ($result | to json)"
$result
}
# Conditional debugging
def create-server [name: string] {
if $env.PROVISIONING_DEBUG? == "true" {
print $"Debug: Creating server ($name)"
}
# Implementation
}
# Interactive debugging
def debug-interactive [] {
print "🐛 Entering debug mode..."
print "Available commands: $env.PATH"
print "Current config: " (get-config | to json)
# Drop into interactive shell
nu --interactive
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Error Investigation< / strong > :< / p >
< pre > < code class = "language-nushell" > # Comprehensive error handling
2025-12-11 21:50:42 +00:00
def safe-server-creation [name: string] {
try {
create-server $name
} catch { |e|
# Log error details
{
timestamp: (date now | format date "%Y-%m-%d %H:%M:%S"),
operation: "create-server",
input: $name,
error: $e.msg,
debug: $e.debug?,
env: {
user: $env.USER,
workspace: $env.PROVISIONING_WORKSPACE_USER?,
debug: $env.PROVISIONING_DEBUG?
}
} | save --append logs/error-debug.json
# Re-throw with context
error make {
msg: $"Server creation failed: ($e.msg)",
label: {text: "failed here", span: $e.span?}
}
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "rust-debugging" > < a class = "header" href = "#rust-debugging" > Rust Debugging< / a > < / h3 >
< p > < strong > Debug Logging< / strong > :< / p >
< pre > < code class = "language-rust" > use tracing::{debug, info, warn, error, instrument};
2025-12-11 21:50:42 +00:00
#[instrument]
pub async fn create_server(name: & str) -> Result< ServerInfo> {
debug!("Starting server creation for: {}", name);
let config = load_config()
.map_err(|e| {
error!("Failed to load config: {:?}", e);
e
})?;
info!("Configuration loaded successfully");
debug!("Config details: {:?}", config);
let server = provision_server(name, & config).await
.map_err(|e| {
error!("Provisioning failed for {}: {:?}", name, e);
e
})?;
info!("Server {} created successfully", name);
Ok(server)
2026-01-12 04:42:18 +00:00
}< / code > < / pre >
< p > < strong > Interactive Debugging< / strong > :< / p >
< pre > < code class = "language-rust" > // Use debugger breakpoints
2025-12-11 21:50:42 +00:00
#[cfg(debug_assertions)]
{
println!("Debug: server creation starting");
dbg!(& config);
// Add breakpoint here in IDE
2026-01-12 04:42:18 +00:00
}< / code > < / pre >
< h3 id = "log-analysis" > < a class = "header" href = "#log-analysis" > Log Analysis< / a > < / h3 >
< p > < strong > Log Monitoring< / strong > :< / p >
< pre > < code class = "language-bash" > # Follow all logs
2025-12-11 21:50:42 +00:00
tail -f workspace/runtime/logs/$USER/*.log
# Filter for errors
grep -i error workspace/runtime/logs/$USER/*.log
# Monitor specific component
tail -f workspace/runtime/logs/$USER/orchestrator.log | grep -i workflow
# Structured log analysis
jq '.level == "ERROR"' workspace/runtime/logs/$USER/structured.jsonl
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Debug Log Levels< / strong > :< / p >
< pre > < code class = "language-bash" > # Different verbosity levels
2025-12-11 21:50:42 +00:00
PROVISIONING_LOG_LEVEL=trace provisioning server create test
PROVISIONING_LOG_LEVEL=debug provisioning server create test
PROVISIONING_LOG_LEVEL=info provisioning server create test
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "integration-workflows" > < a class = "header" href = "#integration-workflows" > Integration Workflows< / a > < / h2 >
< h3 id = "existing-system-integration" > < a class = "header" href = "#existing-system-integration" > Existing System Integration< / a > < / h3 >
< p > < strong > Working with Legacy Components< / strong > :< / p >
< pre > < code class = "language-bash" > # Test integration with existing system
2025-12-11 21:50:42 +00:00
provisioning --version # Legacy system
src/core/nulib/provisioning --version # New system
# Test workspace integration
PROVISIONING_WORKSPACE_USER=$USER provisioning server list
# Validate configuration compatibility
provisioning validate config
nu workspace.nu config validate
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "api-integration-testing" > < a class = "header" href = "#api-integration-testing" > API Integration Testing< / a > < / h3 >
< p > < strong > REST API Testing< / strong > :< / p >
< pre > < code class = "language-bash" > # Test orchestrator API
2025-12-11 21:50:42 +00:00
curl -X GET http://localhost:9090/health
curl -X GET http://localhost:9090/tasks
# Test workflow creation
curl -X POST http://localhost:9090/workflows/servers/create \
-H "Content-Type: application/json" \
2026-01-12 04:42:18 +00:00
-d '{"name": "test-server", "plan": "2xCPU-4 GB"}'
2025-12-11 21:50:42 +00:00
# Monitor workflow
curl -X GET http://localhost:9090/workflows/batch/status/workflow-id
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "database-integration" > < a class = "header" href = "#database-integration" > Database Integration< / a > < / h3 >
< p > < strong > SurrealDB Integration< / strong > :< / p >
< pre > < code class = "language-nushell" > # Test database connectivity
2025-12-11 21:50:42 +00:00
use core/nulib/lib_provisioning/database/surreal.nu
let db = (connect-database)
(test-connection $db)
# Workflow state testing
let workflow_id = (create-workflow-record "test-workflow")
let status = (get-workflow-status $workflow_id)
assert ($status.status == "pending")
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "external-tool-integration" > < a class = "header" href = "#external-tool-integration" > External Tool Integration< / a > < / h3 >
< p > < strong > Container Integration< / strong > :< / p >
< pre > < code class = "language-bash" > # Test with Docker
2025-12-11 21:50:42 +00:00
docker run --rm -v $(pwd):/work provisioning:dev provisioning --version
# Test with Kubernetes
kubectl apply -f manifests/test-pod.yaml
kubectl logs test-pod
# Validate in different environments
make test-dist PLATFORM=docker
make test-dist PLATFORM=kubernetes
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "collaboration-guidelines" > < a class = "header" href = "#collaboration-guidelines" > Collaboration Guidelines< / a > < / h2 >
< h3 id = "branch-strategy" > < a class = "header" href = "#branch-strategy" > Branch Strategy< / a > < / h3 >
< p > < strong > Branch Naming< / strong > :< / p >
< ul >
< li > < code > feature/description< / code > - New features< / li >
< li > < code > fix/description< / code > - Bug fixes< / li >
< li > < code > docs/description< / code > - Documentation updates< / li >
< li > < code > refactor/description< / code > - Code refactoring< / li >
< li > < code > test/description< / code > - Test improvements< / li >
< / ul >
< p > < strong > Workflow< / strong > :< / p >
< pre > < code class = "language-bash" > # Start new feature
2025-12-11 21:50:42 +00:00
git checkout main
git pull origin main
git checkout -b feature/new-provider-support
# Regular commits
git add .
git commit -m "feat(provider): implement server creation API"
# Push and create PR
git push origin feature/new-provider-support
gh pr create --title "Add new provider support" --body "..."
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "code-review-process" > < a class = "header" href = "#code-review-process" > Code Review Process< / a > < / h3 >
< p > < strong > Review Checklist< / strong > :< / p >
< ul >
< li > < input disabled = "" type = "checkbox" / >
Code follows project conventions< / li >
< li > < input disabled = "" type = "checkbox" / >
Tests are included and passing< / li >
< li > < input disabled = "" type = "checkbox" / >
Documentation is updated< / li >
< li > < input disabled = "" type = "checkbox" / >
No hardcoded values< / li >
< li > < input disabled = "" type = "checkbox" / >
Error handling is comprehensive< / li >
< li > < input disabled = "" type = "checkbox" / >
Performance considerations addressed< / li >
< / ul >
< p > < strong > Review Commands< / strong > :< / p >
< pre > < code class = "language-bash" > # Test PR locally
2025-12-11 21:50:42 +00:00
gh pr checkout 123
cd src/tools & & make ci-test
# Run specific tests
nu workspace/extensions/providers/new-provider/tests/run-all.nu
# Check code quality
cargo clippy -- -D warnings
nu --check $(find . -name "*.nu")
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "documentation-requirements" > < a class = "header" href = "#documentation-requirements" > Documentation Requirements< / a > < / h3 >
< p > < strong > Code Documentation< / strong > :< / p >
< pre > < code class = "language-nushell" > # Function documentation
2025-12-11 21:50:42 +00:00
def create-server [
name: string # Server name (must be unique)
2026-01-12 04:42:18 +00:00
plan: string # Server plan (for example, "2xCPU-4 GB")
2025-12-11 21:50:42 +00:00
--dry-run: bool # Show what would be created without doing it
] -> record { # Returns server creation result
# Creates a new server with the specified configuration
#
# Examples:
2026-01-12 04:42:18 +00:00
# create-server "web-01" "2xCPU-4 GB"
# create-server "test" "1xCPU-2 GB" --dry-run
2025-12-11 21:50:42 +00:00
# Implementation
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "communication" > < a class = "header" href = "#communication" > Communication< / a > < / h3 >
< p > < strong > Progress Updates< / strong > :< / p >
< ul >
< li > Daily standup participation< / li >
< li > Weekly architecture reviews< / li >
< li > PR descriptions with context< / li >
< li > Issue tracking with details< / li >
< / ul >
< p > < strong > Knowledge Sharing< / strong > :< / p >
< ul >
< li > Technical blog posts< / li >
< li > Architecture decision records< / li >
< li > Code review discussions< / li >
< li > Team documentation updates< / li >
< / ul >
< h2 id = "quality-assurance" > < a class = "header" href = "#quality-assurance" > Quality Assurance< / a > < / h2 >
< h3 id = "code-quality-checks" > < a class = "header" href = "#code-quality-checks" > Code Quality Checks< / a > < / h3 >
< p > < strong > Automated Quality Gates< / strong > :< / p >
< pre > < code class = "language-bash" > # Pre-commit hooks
2025-12-11 21:50:42 +00:00
pre-commit install
# Manual quality check
cd src/tools
make validate-all
# Security audit
cargo audit
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Quality Metrics< / strong > :< / p >
< ul >
< li > Code coverage > 80%< / li >
< li > No critical security vulnerabilities< / li >
< li > All tests passing< / li >
< li > Documentation coverage complete< / li >
< li > Performance benchmarks met< / li >
< / ul >
< h3 id = "performance-monitoring" > < a class = "header" href = "#performance-monitoring" > Performance Monitoring< / a > < / h3 >
< p > < strong > Performance Testing< / strong > :< / p >
< pre > < code class = "language-bash" > # Benchmark builds
2025-12-11 21:50:42 +00:00
make benchmark
# Performance profiling
cargo flamegraph --bin provisioning-orchestrator
# Load testing
ab -n 1000 -c 10 http://localhost:9090/health
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Resource Monitoring< / strong > :< / p >
< pre > < code class = "language-bash" > # Monitor during development
2025-12-11 21:50:42 +00:00
nu workspace/tools/runtime-manager.nu monitor --duration 5m
# Check resource usage
du -sh workspace/runtime/
df -h
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "best-practices" > < a class = "header" href = "#best-practices" > Best Practices< / a > < / h2 >
< h3 id = "configuration-management" > < a class = "header" href = "#configuration-management" > Configuration Management< / a > < / h3 >
< p > < strong > Never Hardcode< / strong > :< / p >
< pre > < code class = "language-nushell" > # Bad
2025-12-11 21:50:42 +00:00
def get-api-url [] { "https://api.upcloud.com" }
# Good
def get-api-url [] {
get-config-value "providers.upcloud.api_url" "https://api.upcloud.com"
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "error-handling" > < a class = "header" href = "#error-handling" > Error Handling< / a > < / h3 >
< p > < strong > Comprehensive Error Context< / strong > :< / p >
< pre > < code class = "language-nushell" > def create-server [name: string] {
2025-12-11 21:50:42 +00:00
try {
validate-server-name $name
} catch { |e|
error make {
msg: $"Invalid server name '($name)': ($e.msg)",
label: {text: "server name validation failed", span: $e.span?}
}
}
try {
provision-server $name
} catch { |e|
error make {
msg: $"Server provisioning failed for '($name)': ($e.msg)",
help: "Check provider credentials and quota limits"
}
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "resource-management" > < a class = "header" href = "#resource-management" > Resource Management< / a > < / h3 >
< p > < strong > Clean Up Resources< / strong > :< / p >
< pre > < code class = "language-nushell" > def with-temporary-server [name: string, action: closure] {
2025-12-11 21:50:42 +00:00
let server = (create-server $name)
try {
do $action $server
} catch { |e|
# Clean up on error
delete-server $name
$e
}
# Clean up on success
delete-server $name
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "testing-best-practices" > < a class = "header" href = "#testing-best-practices" > Testing Best Practices< / a > < / h3 >
< p > < strong > Test Isolation< / strong > :< / p >
< pre > < code class = "language-nushell" > def test-with-isolation [test_name: string, test_action: closure] {
2025-12-11 21:50:42 +00:00
let test_workspace = $"test-($test_name)-(date now | format date '%Y%m%d%H%M%S')"
try {
# Set up isolated environment
$env.PROVISIONING_WORKSPACE_USER = $test_workspace
nu workspace.nu init --user-name $test_workspace
# Run test
do $test_action
print $"✅ Test ($test_name) passed"
} catch { |e|
print $"❌ Test ($test_name) failed: ($e.msg)"
exit 1
} finally {
# Clean up test environment
nu workspace.nu cleanup --user-name $test_workspace --type all --force
}
}
< / code > < / pre >
2026-01-14 01:56:30 +00:00
< p > This development workflow provides a comprehensive framework for efficient, quality-focused development while maintaining the project’ s architectural
principles and ensuring smooth collaboration across the team.< / p >
2025-12-11 21:50:42 +00:00
< / main >
< nav class = "nav-wrapper" aria-label = "Page navigation" >
<!-- Mobile navigation buttons -->
2026-01-14 01:56:30 +00:00
< a rel = "prev" href = "../development/command-handler-guide.html" class = "mobile-nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
2025-12-11 21:50:42 +00:00
< i class = "fa fa-angle-left" > < / i >
< / a >
< a rel = "next prefetch" href = "../development/integration.html" class = "mobile-nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
< i class = "fa fa-angle-right" > < / i >
< / a >
< div style = "clear: both" > < / div >
< / nav >
< / div >
< / div >
< nav class = "nav-wide-wrapper" aria-label = "Page navigation" >
2026-01-14 01:56:30 +00:00
< a rel = "prev" href = "../development/command-handler-guide.html" class = "nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
2025-12-11 21:50:42 +00:00
< i class = "fa fa-angle-left" > < / i >
< / a >
< a rel = "next prefetch" href = "../development/integration.html" class = "nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
< i class = "fa fa-angle-right" > < / i >
< / a >
< / nav >
< / div >
< script >
window.playground_copyable = true;
< / script >
< script src = "../elasticlunr.min.js" > < / script >
< script src = "../mark.min.js" > < / script >
< script src = "../searcher.js" > < / script >
< script src = "../clipboard.min.js" > < / script >
< script src = "../highlight.js" > < / script >
< script src = "../book.js" > < / script >
<!-- Custom JS scripts -->
< / div >
< / body >
< / html >