#!/usr/bin/env nu # KCL Core Packaging System # Creates distributable packages for core provisioning/kcl module use ../core/nulib/lib_provisioning/config/accessor.nu * # Main packaging command def main [subcommand?: string] { if ($subcommand | is-empty) { print_help return } match $subcommand { "build" => print_build_help "install" => print_install_help "version" => print_version_help "clean" => print_clean_help "help" => print_help _ => { print $"Unknown command: ($subcommand)" print_help } } } # Build core provisioning package export def "main build" [ --version = "0.0.1", # Package version --output = "dist", # Output directory --format = "tar.gz", # Package format: tar.gz, zip --include-docs = false, # Include documentation --clean = true # Clean before build ]: nothing -> record { print $"Building core provisioning KCL package v($version)" # Clean previous builds if requested if $clean { clean-build-artifacts $output } # Validate source structure let validation = validate-source-structure if not $validation.valid { error make { msg: $"Source validation failed: ($validation.errors)" } } # Create package structure let package_info = create-package-structure $output $version # Copy KCL files copy-kcl-sources $package_info.package_dir # Generate package metadata generate-package-metadata $package_info.package_dir $version # Create archive let archive_path = create-package-archive $package_info.package_dir $format $output $version print $"✅ Package built successfully: ($archive_path)" { version: $version format: $format archive: $archive_path size: (ls $archive_path | get 0.size) package_dir: $package_info.package_dir checksum: (generate-checksum $archive_path) } } # Install package to target location export def "main install" [ package_path: string, # Path to package archive target = "~/.kcl/modules", # Installation target --force = false, # Force overwrite --verify = true # Verify installation ]: nothing -> record { print $"Installing KCL package: ($package_path) to ($target)" # Validate package if not ($package_path | path exists) { error make { msg: $"Package not found: ($package_path)" } } # Extract package info let package_info = extract-package-info $package_path # Check if already installed let install_path = ($target | path join "provisioning" $package_info.version) if ($install_path | path exists) and (not $force) { error make { msg: $"Package already installed at ($install_path). Use --force to overwrite." } } # Create installation directory mkdir ($target | path join "provisioning") # Extract package extract-package $package_path $install_path # Verify installation if $verify { let verification = verify-installation $install_path if not $verification.valid { error make { msg: $"Installation verification failed: ($verification.errors)" } } } print $"✅ Package installed successfully to: ($install_path)" { package: $package_path version: $package_info.version install_path: $install_path verified: $verify } } # Show package version info export def "main version" [ package_path?: string # Optional package path, defaults to current source ]: nothing -> record { if ($package_path | is-empty) { # Show current source version let kcl_path = "../kcl" if not ($kcl_path | path exists) { error make { msg: "KCL source directory not found" } } let version_file = ($kcl_path | path join "version.k") if ($version_file | path exists) { let content = (open $version_file) extract-version-from-content $content } else { { version: "0.0.1", source: "default" } } } else { # Extract version from package extract-package-info $package_path } } # Clean build artifacts export def "main clean" [ output_dir = "dist" # Output directory to clean ]: nothing -> record { print $"Cleaning build artifacts from: ($output_dir)" if ($output_dir | path exists) { let artifacts = (ls $output_dir | length) rm -rf $output_dir print $"✅ Removed ($artifacts) artifacts" { cleaned: $artifacts, directory: $output_dir } } else { print "No artifacts to clean" { cleaned: 0, directory: $output_dir } } } # Validate source structure def validate-source-structure []: nothing -> record { let kcl_path = "../kcl" let required_files = [ "main.k" "settings.k" "defaults.k" "server.k" "cluster.k" "workflows.k" "version.k" ] let missing = ($required_files | where { not (($kcl_path | path join $in) | path exists) }) { valid: ($missing | is-empty), missing_files: $missing, errors: (if ($missing | is-empty) { [] } else { [$"Missing required files: ($missing | str join ', ')"] }) } } # Create package directory structure def create-package-structure [output: string, version: string]: nothing -> record { let package_name = $"provisioning-kcl-($version)" let package_dir = ($output | path join $package_name) # Create directories mkdir $output mkdir $package_dir mkdir ($package_dir | path join "kcl") mkdir ($package_dir | path join "docs") mkdir ($package_dir | path join "examples") { package_name: $package_name package_dir: $package_dir output_dir: $output } } # Copy KCL source files def copy-kcl-sources [package_dir: string] { let source_kcl = "../kcl" let target_kcl = ($package_dir | path join "kcl") # Copy all .k files let kcl_files = (glob ($source_kcl | path join "*.k")) for $file in $kcl_files { let filename = ($file | path basename) cp $file ($target_kcl | path join $filename) } # Copy kcl.mod if exists let kcl_mod = ($source_kcl | path join "kcl.mod") if ($kcl_mod | path exists) { cp $kcl_mod ($target_kcl | path join "kcl.mod") } print $"Copied (($kcl_files | length)) KCL files" } # Generate package metadata def generate-package-metadata [package_dir: string, version: string] { let metadata = { name: "provisioning" version: $version description: "Core provisioning KCL schemas and configurations" author: "Provisioning Team" license: "MIT" created: (date now | format date '%Y-%m-%d %H:%M:%S') kcl_version: "0.11.2" dependencies: {} files: { schemas: (glob ($package_dir | path join "kcl" "*.k") | each { path basename }) main: "main.k" } } $metadata | to json | save ($package_dir | path join "package.json") $metadata | to yaml | save ($package_dir | path join "package.yaml") # Create README let readme_content = $"# Provisioning KCL Package Version: ($version) Created: ($metadata.created) ## Description ($metadata.description) ## Installation ```bash kcl-packager install provisioning-kcl-($version).tar.gz ``` ## Usage ```kcl import provisioning # Use provisioning schemas settings: provisioning.Settings = { # Your configuration } ``` ## Files - main.k: Main entry point - settings.k: Core settings schema - defaults.k: Default configurations - server.k: Server management schemas - cluster.k: Cluster configuration schemas - workflows.k: Workflow definitions " $readme_content | save ($package_dir | path join "README.md") } # Create package archive def create-package-archive [package_dir: string, format: string, output: string, version: string]: nothing -> string { let package_name = ($package_dir | path basename) let archive_name = match $format { "tar.gz" => $"($package_name).tar.gz" "zip" => $"($package_name).zip" _ => $"($package_name).tar.gz" } let archive_path = ($output | path join $archive_name) # Create archive match $format { "tar.gz" => { cd $output ^tar czf $archive_name $package_name } "zip" => { cd $output ^zip -r $archive_name $package_name } _ => { cd $output ^tar czf $archive_name $package_name } } $archive_path } # Generate file checksum def generate-checksum [file_path: string]: nothing -> string { if (which sha256sum | is-empty) { if (which shasum | is-empty) { "checksum-unavailable" } else { ^shasum -a 256 $file_path | split row ' ' | get 0 } } else { ^sha256sum $file_path | split row ' ' | get 0 } } # Extract package information def extract-package-info [package_path: string]: nothing -> record { # For now, extract from filename let filename = ($package_path | path basename) let parts = ($filename | str replace '.tar.gz' '' | str replace '.zip' '' | split row '-') if ($parts | length) >= 3 { { name: ($parts | take 2 | str join '-'), version: ($parts | get 2), format: (if ($filename | str contains '.tar.gz') { "tar.gz" } else { "zip" }) } } else { { name: "unknown", version: "unknown", format: "unknown" } } } # Extract package to target location def extract-package [package_path: string, target_path: string] { let format = if ($package_path | str contains '.tar.gz') { "tar.gz" } else { "zip" } mkdir $target_path match $format { "tar.gz" => { ^tar xzf $package_path -C $target_path --strip-components=1 } "zip" => { ^unzip -q $package_path -d ($target_path | path dirname) # Move extracted contents up one level let extracted_dir = (ls ($target_path | path dirname) | where type == dir | get name | first) mv ($extracted_dir | path join "*") $target_path rm -rf $extracted_dir } } } # Verify installation def verify-installation [install_path: string]: nothing -> record { let required_files = ["kcl/main.k", "package.json"] let missing = ($required_files | where { not (($install_path | path join $in) | path exists) }) { valid: ($missing | is-empty), missing_files: $missing, errors: (if ($missing | is-empty) { [] } else { [$"Missing files after installation: ($missing | str join ', ')"] }) } } # Extract version from KCL content def extract-version-from-content [content: string]: nothing -> record { let lines = ($content | lines) let version_line = ($lines | where ($it | str contains "version") | first | default "") if ($version_line | is-empty) { { version: "0.0.1", source: "default" } } else { let version = ($version_line | str replace 'version = "' '' | str replace '"' '' | str trim) { version: $version, source: "file" } } } # Clean build artifacts def clean-build-artifacts [output_dir: string] { if ($output_dir | path exists) { print $"Cleaning previous build artifacts from: ($output_dir)" rm -rf $output_dir } mkdir $output_dir } # Help functions def print_help [] { print "KCL Core Packaging System - Build and distribute core provisioning KCL packages" print "" print "Usage: kcl-packager [options]" print "" print "Commands:" print " build [options] - Build package from source" print " install [target] - Install package to target location" print " version [package] - Show version information" print " clean [output] - Clean build artifacts" print " help - Show this help" print "" print "Examples:" print " kcl-packager build --version 1.0.0" print " kcl-packager install dist/provisioning-kcl-1.0.0.tar.gz" print " kcl-packager version" } def print_build_help [] { print "Build core provisioning KCL package" print "" print "Usage: kcl-packager build [options]" print "" print "Options:" print " --version Package version (default: 0.0.1)" print " --output Output directory (default: dist)" print " --format Package format: tar.gz, zip (default: tar.gz)" print " --include-docs Include documentation" print " --clean Clean before build (default: true)" print "" print "Examples:" print " kcl-packager build --version 1.0.0 --format zip" print " kcl-packager build --output packages --include-docs" } def print_install_help [] { print "Install KCL package to target location" print "" print "Usage: kcl-packager install [target] [options]" print "" print "Arguments:" print " package Path to package archive" print " target Installation target (default: ~/.kcl/modules)" print "" print "Options:" print " --force Force overwrite existing installation" print " --verify Verify installation (default: true)" print "" print "Examples:" print " kcl-packager install dist/provisioning-kcl-1.0.0.tar.gz" print " kcl-packager install package.tar.gz /usr/local/kcl/modules --force" } def print_version_help [] { print "Show package version information" print "" print "Usage: kcl-packager version [package]" print "" print "Arguments:" print " package Optional package path (defaults to current source)" print "" print "Examples:" print " kcl-packager version # Show source version" print " kcl-packager version dist/provisioning-kcl-1.0.0.tar.gz # Show package version" } def print_clean_help [] { print "Clean build artifacts" print "" print "Usage: kcl-packager clean [output]" print "" print "Arguments:" print " output Output directory to clean (default: dist)" print "" print "Examples:" print " kcl-packager clean" print " kcl-packager clean packages" }