nushell-plugins/scripts/create_full_distribution.nu
Jesús Pérez 4b92aa764a
Some checks failed
Build and Test / Validate Setup (push) Has been cancelled
Build and Test / Build (darwin-amd64) (push) Has been cancelled
Build and Test / Build (darwin-arm64) (push) Has been cancelled
Build and Test / Build (linux-amd64) (push) Has been cancelled
Build and Test / Build (windows-amd64) (push) Has been cancelled
Build and Test / Build (linux-arm64) (push) Has been cancelled
Build and Test / Security Audit (push) Has been cancelled
Build and Test / Package Results (push) Has been cancelled
Build and Test / Quality Gate (push) Has been cancelled
implements a production-ready bootstrap installer with comprehensive error handling, version-agnostic archive extraction, and clear user messaging. All improvements follow DRY principles using symlink-based architecture for single-source-of-truth maintenance
2025-12-11 22:04:54 +00:00

595 lines
20 KiB
Plaintext
Executable File

#!/usr/bin/env nu
# Create Full Distribution - Complete Packaging Workflow
# Creates both full distributions (nushell + plugins) and bin archives (plugins only)
#
# Usage:
# create_full_distribution.nu # Current platform
# create_full_distribution.nu --all-platforms # All platforms
# create_full_distribution.nu --bin-only # Only create bin archives
use lib/common_lib.nu *
# Main entry point
def main [
--all-platforms # Create packages for all platforms
--bin-only # Only create bin archives
--checksums # Generate SHA256 checksums
--verify # Verify packages after creation
] {
# CRITICAL: Ensure we're in the repository root directory
let current_dir = $env.PWD
# Check if we're already in the right directory
let is_repo_root = (
("nu_plugin_auth" | path exists) and
("nushell" | path exists) and
("scripts" | path exists)
)
if not $is_repo_root {
# Try to find the repository root by looking for the marker files
let script_parent = $current_dir
let potential_root = ($script_parent | path dirname)
if not (($potential_root | path join "nu_plugin_auth" | path exists) and
($potential_root | path join "nushell" | path exists)) {
log_error $"❌ Cannot find repository root from current directory: ($current_dir)"
log_error "Expected to find: nu_plugin_* directories and nushell/ directory"
exit 1
}
try {
cd $potential_root
log_info $"✅ Changed directory to repository root: ($potential_root)"
} catch {
log_error $"Failed to change to repository root: ($potential_root)"
exit 1
}
} else {
log_info $"✅ Already in repository root: ($current_dir)"
}
print_banner
if not $bin_only {
# Phase 1: Collect binaries
print "╔══════════════════════════════════════════════════════════╗"
print "║ Phase 1: Collect Binaries ║"
print "╚══════════════════════════════════════════════════════════╝"
collect_binaries $all_platforms
}
# Phase 2: Create full distribution packages
if not $bin_only {
print ""
print "╔══════════════════════════════════════════════════════════╗"
print "║ Phase 2: Create Full Distribution Packages ║"
print "╚══════════════════════════════════════════════════════════╝"
create_distribution_packages $all_platforms $checksums
}
# Phase 3: Create bin archives
print ""
print "╔══════════════════════════════════════════════════════════╗"
print "║ Phase 3: Create Bin Archives ║"
print "╚══════════════════════════════════════════════════════════╝"
create_bin_archives
# Phase 4: Verification
if $verify {
print ""
print "╔══════════════════════════════════════════════════════════╗"
print "║ Phase 4: Verify Packages ║"
print "╚══════════════════════════════════════════════════════════╝"
verify_packages
}
# Final summary
generate_distribution_summary $all_platforms $bin_only
}
# Print banner
def print_banner [] {
print $"
(ansi blue)╔════════════════════════════════════════════════════════════╗
║ ║
║ 📦 Complete Distribution Creator 📦 ║
║ ║
║ Creates: ║
║ • Full distribution packages \(nu + plugins\) ║
║ • Bin archives \(plugins only\) ║
║ • Checksums and manifests ║
║ ║
╚════════════════════════════════════════════════════════════╝(ansi reset)
"
}
# Collect binaries for distribution
def collect_binaries [all_platforms: bool] {
log_info "📥 Collecting binaries..."
if ("./scripts/collect_full_binaries.nu" | path exists) {
# Use built nu binary, not system nu (which may be broken)
let built_nu = "./nushell/target/release/nu"
let nu_bin = if ($built_nu | path exists) { $built_nu } else { "nu" }
let result = (do {
if $all_platforms {
^$nu_bin ./scripts/collect_full_binaries.nu --all-platforms --force
} else {
^$nu_bin ./scripts/collect_full_binaries.nu --force
}
} | complete)
print $result.stdout
if ($result.stderr | is-not-empty) {
print $result.stderr
}
if $result.exit_code == 0 {
log_success "Binaries collected successfully"
} else {
log_error $"Failed to collect binaries \(exit code: ($result.exit_code)\)"
if ($result.stderr | is-not-empty) {
log_error $result.stderr
}
exit 1
}
} else {
log_warn "collect_full_binaries.nu not found, using fallback..."
collect_binaries_fallback
}
}
# Fallback binary collection
def collect_binaries_fallback [] {
let platform = detect_platform
log_info $"Collecting for platform: ($platform)"
# Create distribution directory
let dist_dir = $"./distribution/($platform)"
ensure_dir $dist_dir
# Copy nushell binary
if ("./nushell/target/release/nu" | path exists) {
cp ./nushell/target/release/nu $dist_dir
log_success "Copied nushell binary"
} else {
log_error "Nushell binary not found. Build it first with: just build-nushell"
exit 1
}
# Copy system plugins
let system_plugins = (try { ls ./nushell/target/release/nu_plugin_* } catch { [] } | where type == file)
for plugin in $system_plugins {
cp $plugin.name $dist_dir
}
if ($system_plugins | length) > 0 {
log_success $"Copied ($system_plugins | length) system plugins"
}
# Copy custom plugins
let custom_plugins = (try { ls nu_plugin_*/target/release/nu_plugin_* } catch { [] } | where type == file)
for plugin in $custom_plugins {
cp $plugin.name $dist_dir
}
if ($custom_plugins | length) > 0 {
log_success $"Copied ($custom_plugins | length) custom plugins"
}
let total = 1 + ($system_plugins | length) + ($custom_plugins | length)
log_success $"Total binaries collected: ($total)"
}
# Create distribution packages
def create_distribution_packages [
all_platforms: bool
checksums: bool
] {
log_info "📦 Creating distribution packages..."
if ("./scripts/create_distribution_packages.nu" | path exists) {
# Use project binary for consistency
let built_nu = "./nushell/target/release/nu"
let nu_bin = if ($built_nu | path exists) { $built_nu } else { "nu" }
let result = (do {
if $all_platforms and $checksums {
^$nu_bin ./scripts/create_distribution_packages.nu --all-platforms --checksums
} else if $all_platforms {
^$nu_bin ./scripts/create_distribution_packages.nu --all-platforms
} else if $checksums {
^$nu_bin ./scripts/create_distribution_packages.nu --checksums
} else {
^$nu_bin ./scripts/create_distribution_packages.nu
}
} | complete)
print $result.stdout
if $result.exit_code == 0 {
log_success "Distribution packages created"
}
} else {
log_warn "create_distribution_packages.nu not found, using justfile..."
let pack_result = (do {
if $all_platforms {
just pack-full-all
} else {
just pack-full
}
} | complete)
if $pack_result.exit_code == 0 {
log_success "Packages created via justfile"
}
}
}
# Create bin archives (plugin-only) - directly from built plugins
def create_bin_archives [] {
log_info "📦 Creating bin archives (plugin-only)..."
# Create output directory
if not ("./bin_archives" | path exists) {
mkdir ./bin_archives
}
# Get version for naming
let version = try {
open ./nushell/Cargo.toml | get package.version
} catch {
"unknown"
}
# Get actually built platforms from distribution directory
let built_platforms = try {
ls ./distribution/*/nu | each {|p| $p.name | path dirname | path basename}
} catch {
[]
}
# If no platforms found, skip Phase 3
if ($built_platforms | length) == 0 {
log_warn "No platforms found in distribution directory - skipping plugin archive creation"
return
}
# Create archive for each built platform
for platform in $built_platforms {
let archive_name = $"plugins-only-($version)-($platform).tar.gz"
let archive_path = (pwd | append "bin_archives" | path join | append $archive_name | path join)
# Create temporary directory for archive contents
let temp_dir = (pwd | append "bin_archives" | path join | append $"plugins-temp-($version)-($platform)" | path join)
mkdir $temp_dir
# Collect all built plugin binaries (both custom and workspace plugins)
log_info $"📦 Collecting plugins for ($platform)..."
# Get custom plugin binaries from nu_plugin_*/target/release/
let custom_plugins = (
try {
ls nu_plugin_*/target/release/nu_plugin_*
| where type == "file"
| where ($it.name | str ends-with ".d") == false # Exclude .d dependency files
} catch {
[]
}
)
# Get workspace plugin binaries from nushell/target/release/
let workspace_plugins = (
try {
ls nushell/target/release/nu_plugin_*
| where type == "file"
| where ($it.name | str ends-with ".d") == false # Exclude .d dependency files
} catch {
[]
}
)
# Combine both lists
let plugins_to_copy = ($custom_plugins | append $workspace_plugins)
let plugin_count = ($plugins_to_copy | length)
if $plugin_count > 0 {
log_success $"Collected ($plugin_count) plugins"
# Copy plugins to temp directory
try {
$plugins_to_copy | each {|p|
cp $p.name $temp_dir
}
} catch {
log_error "Failed to copy plugins to temp directory"
}
# Create archive using absolute path (tar -C changes directory safely)
log_info "Creating archive..."
let tar_result = (do {
tar -C $temp_dir -czf $archive_path .
} | complete)
if $tar_result.exit_code == 0 {
if ($archive_path | path exists) {
let size = (ls $archive_path | get 0.size) / 1024 / 1024
log_success $"Archive created: ($archive_name) - (($size | into int))MB"
} else {
log_error $"Archive file exists but cannot be accessed: ($archive_path)"
}
} else {
log_error $"Failed to create archive. Exit code: ($tar_result.exit_code)"
if ($tar_result.stderr | is-not-empty) {
log_error $"Tar error: ($tar_result.stderr)"
}
}
} else {
log_warn $"No plugins found to package for ($platform)"
}
# Clean up temp directory
if ($temp_dir | path exists) {
try {
rm -r $temp_dir
} catch {
log_warn "Could not clean up temporary directory"
}
}
}
}
# Verify packages
def verify_packages [] {
log_info "✅ Verifying packages..."
# Check distribution packages
if ("./distribution/packages" | path exists) {
let packages = (try { ls ./distribution/packages/*.tar.gz ./distribution/packages/*.zip } catch { [] })
if ($packages | length) > 0 {
log_success $"Found ($packages | length) distribution packages"
for pkg in $packages {
let name = $pkg.name | path basename
let size = $pkg.size / 1024 / 1024
if $size < 1 {
log_error $" ✗ ($name): Too small (($size | into int)MB)"
} else if $size > 500 {
log_warn $" ⚠ ($name): Very large (($size | into int)MB)"
} else {
log_success $" ✓ ($name): (($size | into int)MB)"
}
}
} else {
log_warn "No distribution packages found"
}
}
# Check bin archives
if ("./bin_archives" | path exists) {
let archives = (try { ls ./bin_archives/*.tar.gz } catch { [] })
if ($archives | length) > 0 {
log_success $"Found ($archives | length) bin archives"
} else {
log_warn "No bin archives found"
}
}
# Verify checksums if present
if ("./distribution/packages/checksums.txt" | path exists) {
log_info "Verifying checksums..."
let check_result = (do {
cd distribution/packages
shasum -c checksums.txt
} | complete)
if $check_result.exit_code == 0 {
log_success "All checksums verified"
} else {
log_error "Checksum verification failed"
}
}
}
# Generate distribution summary
def generate_distribution_summary [
all_platforms: bool
bin_only: bool
] {
log_info "\n📊 Distribution Summary\n"
# Get nushell version
let version = if ("./nushell/Cargo.toml" | path exists) {
open ./nushell/Cargo.toml | get package.version
} else {
"unknown"
}
log_success $"Nushell version: ($version)"
# Count distribution packages
if not $bin_only and ("./distribution/packages" | path exists) {
let packages = (try { ls ./distribution/packages/*.tar.gz ./distribution/packages/*.zip } catch { [] })
if ($packages | length) > 0 {
log_success $"Distribution packages: ($packages | length)"
let total_size = ($packages | get size | math sum) / 1024 / 1024
log_info $"Total size: (($total_size | into int))MB"
}
}
# Count bin archives
if ("./bin_archives" | path exists) {
let archives = (try { ls ./bin_archives/*.tar.gz } catch { [] })
if ($archives | length) > 0 {
log_success $"Bin archives: ($archives | length)"
let total_size = ($archives | get size | math sum) / 1024 / 1024
log_info $"Total size: (($total_size | into int))MB"
}
}
# List platforms
if not $bin_only and ("./distribution" | path exists) {
let platforms = (try { ls ./distribution/*/nu } catch { [] } | each {|p| $p.name | path dirname | path basename})
if ($platforms | length) > 0 {
log_info $"\nPlatforms built: ($platforms | str join ', ')"
# Next steps - use actual built platforms
log_info "\n📝 Next Steps:"
log_info " 1. Register plugins:"
# Show example for first built platform
let first_platform = $platforms | first 1 | get 0
log_info $" cd distribution/($first_platform) && nu register-plugins.nu"
}
} else {
# If bin_only, no next steps for distribution
log_info "\n📝 Next Steps:"
}
if not $bin_only {
# Continue with other next steps
}
log_info " 2. Upload to release:"
log_info " gh release create v($version) distribution/packages/*"
log_info " 3. Update documentation:"
log_info " Update README.md with new version"
}
# Detect current platform
def detect_platform []: nothing -> string {
let host_info = sys host
let os = $host_info.name
let arch = (sys cpu | get 0.name)
if $os == "Darwin" {
if $arch == "aarch64" {
"darwin-arm64"
} else {
"darwin-x86_64"
}
} else if $os == "Linux" {
if $arch == "aarch64" {
"linux-arm64"
} else {
"linux-x86_64"
}
} else if $os == "Windows" {
"windows-x86_64"
} else {
$"($os | str downcase)-($arch)"
}
}
# Ensure directory exists
def ensure_dir [dir: string] {
if not ($dir | path exists) {
mkdir $dir
}
}
# Quick command to rebuild and redistribute
def "main rebuild" [] {
log_info "🔄 Rebuild and redistribute workflow\n"
# Build nushell
log_info "Building nushell..."
let build_result = (do {
just build-nushell
} | complete)
if $build_result.exit_code != 0 {
log_error "Nushell build failed"
exit 1
}
# Build plugins
log_info "Building plugins..."
let plugins_result = (do {
just build
} | complete)
if $plugins_result.exit_code != 0 {
log_warn "Some plugins failed to build"
}
# Create distributions
main --checksums
}
# Show distribution status
def "main status" [] {
log_info "📊 Distribution Status\n"
# Check if nushell is built
if ("./nushell/target/release/nu" | path exists) {
let version_result = (do {
./nushell/target/release/nu -c "version | get version"
} | complete)
log_success $"Nushell binary: (str trim $version_result.stdout)"
} else {
log_warn "Nushell binary: Not built"
}
# Check distribution directory
if ("./distribution" | path exists) {
let platforms = (try { ls ./distribution/*/nu } catch { [] })
if ($platforms | length) > 0 {
log_success $"Distribution platforms: ($platforms | length)"
for p in $platforms {
let platform = $p.name | path dirname | path basename
print $" • ($platform)"
}
} else {
log_warn "Distribution: No binaries collected"
}
} else {
log_warn "Distribution: Not created"
}
# Check packages
if ("./distribution/packages" | path exists) {
let packages = (try { ls ./distribution/packages/*.tar.gz ./distribution/packages/*.zip } catch { [] })
if ($packages | length) > 0 {
log_success $"Packages: ($packages | length) created"
} else {
log_warn "Packages: None created"
}
}
# Check bin archives
if ("./bin_archives" | path exists) {
let archives = (try { ls ./bin_archives/*.tar.gz } catch { [] })
if ($archives | length) > 0 {
log_success $"Bin archives: ($archives | length) created"
} else {
log_warn "Bin archives: None created"
}
}
}