#!/usr/bin/env nu # Unpack - Extract and validate distribution bundles # # Usage: # nu scripts/provisioning/unpack.nu bundle.tar.gz # nu scripts/provisioning/unpack.nu --bundle bundle.tar.gz --dest ./extracted # use common/validate.nu * def show-help [] { let help_text = " syntaxis Unpack - Extract and validate distribution bundles USAGE: nu scripts/provisioning/unpack.nu [OPTIONS] [BUNDLE] ARGUMENTS: BUNDLE Bundle file to extract (.tar.gz or .zip) OPTIONS: --bundle Bundle file path (alternative to positional argument) --dest Extraction destination directory Default: current directory --verify Verify checksums after extraction --list-contents Show bundle contents without extracting --help Show this help message EXAMPLES: # Extract bundle to current directory nu scripts/provisioning/unpack.nu bundle.tar.gz # Extract to specific directory nu scripts/provisioning/unpack.nu --bundle bundle.tar.gz --dest ./extracted # List bundle contents nu scripts/provisioning/unpack.nu --list-contents bundle.tar.gz # Extract and verify checksums nu scripts/provisioning/unpack.nu bundle.tar.gz --verify " print $help_text } def detect-bundle-format [bundle_file: path] { let ext = ($bundle_file | path parse | get extension) match $ext { "gz" => { "tar.gz" } "zip" => { "zip" } _ => { let msg = "Unknown bundle format: " + $ext error make {msg: $msg} } } } def extract-bundle [bundle_file: path, dest_dir: path, format: string] { print ("📦 Extracting " + $format + " bundle...") ^mkdir -p $dest_dir match $format { "tar.gz" => { ^tar -xzf $bundle_file -C $dest_dir print "✅ Extraction complete" } "zip" => { ^unzip -q $bundle_file -d $dest_dir print "✅ Extraction complete" } _ => { let msg = "Unsupported format: " + $format error make {msg: $msg} } } } def find-manifest [dest_dir: path] { let manifest_file = ($dest_dir | path join "*" "manifest.toml") let matching_files = (glob $manifest_file) if (($matching_files | length) == 0) { error make {msg: "No manifest found in bundle"} } $matching_files.0 } def show-bundle-info [manifest_file: path] { let manifest = try { open --raw $manifest_file | from toml } catch { let msg = "Failed to read manifest: " + ($manifest_file | into string) error make {msg: $msg} } print "" print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print "📦 Bundle Information" print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print ("Version: " + ($manifest.bundle.version | into string)) print ("Target: " + ($manifest.bundle.target | into string)) print ("Format: " + ($manifest.bundle.format | into string)) print ("Created: " + ($manifest.bundle.created_at | into string)) print "" print "Artifacts:" print (" Binaries: " + (($manifest.artifacts.binaries | length) | into string)) $manifest.artifacts.binaries | each { |bin| print (" - " + ($bin | into string)) } print (" Configs: " + (($manifest.artifacts.configs | length) | into string)) $manifest.artifacts.configs | each { |cfg| print (" - " + ($cfg | into string)) } print (" Docs: " + (($manifest.artifacts.docs | length) | into string)) $manifest.artifacts.docs | each { |doc| print (" - " + ($doc | into string)) } print "" print ("Checksums: " + (($manifest.checksums | length) | into string)) print "" } def verify-bundle [dest_dir: path, manifest_file: path] { print "🔍 Verifying bundle checksums..." let bundle_root = ($manifest_file | path dirname) let checksums_file = ($bundle_root | path join ".." "*.checksums.toml") let matching_checksums = (glob $checksums_file) if (($matching_checksums | length) == 0) { print "⚠️ No checksums file found - skipping verification" return } let checksums_path = $matching_checksums.0 let verify_result = (verify-checksums $checksums_path $bundle_root) if ($verify_result.failed == 0) { print ("✅ All " + ($verify_result.verified | into string) + " checksums verified") } else { print ("❌ " + ($verify_result.failed | into string) + " checksum mismatches found") print "Failed files:" $verify_result.errors | each { |err| print (" - " + $err.file + ": expected " + ($err.expected | str substring 0..8) + "... got " + ($err.actual | str substring 0..8) + "...") } } } def list-bundle-contents [bundle_file: path, format: string] { print ("📋 Contents of " + ($bundle_file | path basename) + ":") print "" match $format { "tar.gz" => { ^tar -tzf $bundle_file | each { |line| print (" " + $line) } } "zip" => { ^unzip -l $bundle_file | lines | skip 3 | drop 2 | each { |line| let trimmed = ($line | str trim) if (($trimmed | str length) > 0) { print (" " + $line) } } } _ => { let msg = "Unsupported format: " + $format error make {msg: $msg} } } print "" } # Main entry point def main [bundle: string = "", --bundle: path = "", --dest: path = ".", --verify = false, --list-contents = false, --help = false] { if $help { show-help return } # Determine bundle file let bundle_file = if ($bundle != "") { $bundle } else if ($bundle != "") { $bundle } else { error make {msg: "Bundle file must be specified"} } if (not (($bundle_file | path exists))) { let msg = "Bundle file not found: " + ($bundle_file | into string) error make {msg: $msg} } # Validate bundle print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print "🔍 syntaxis Unpack" print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print ("Bundle: " + ($bundle_file | path basename)) print ("Size: " + (((ls $bundle_file | get size.0) | into int) / (1024 * 1024) | math round --precision 2 | into string) + " MB") print "" let format = (detect-bundle-format $bundle_file) # Validate bundle structure let validation = (validate-bundle-structure $bundle_file) if (not ($validation.valid)) { print "❌ Bundle validation failed:" $validation.errors | each { |err| print (" - " + $err) } error make {msg: "Invalid bundle"} } print ("✅ Bundle format valid: " + $format) print "" # List contents if requested if $list_contents { list-bundle-contents $bundle_file $format return } # Extract bundle extract-bundle $bundle_file $dest $format # Find and display manifest let manifest_file = (find-manifest $dest) show-bundle-info $manifest_file # Verify if requested if $verify { verify-bundle $dest $manifest_file } print "📂 Extracted to: " + ($dest | into string) print "" }