477 lines
14 KiB
Plaintext
477 lines
14 KiB
Plaintext
|
|
#!/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"
|
||
|
|
}
|