provisioning/tools/kcl-packager.nu

477 lines
14 KiB
Plaintext
Raw Permalink Normal View History

2025-10-07 11:12:02 +01:00
#!/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 <command> [options]"
print ""
print "Commands:"
print " build [options] - Build package from source"
print " install <package> [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 <version> Package version (default: 0.0.1)"
print " --output <dir> Output directory (default: dist)"
print " --format <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 <package> [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"
}