#!/usr/bin/env nu # Collect Install Script # Collects built plugins and prepares them for installation/distribution # Load environment variables from env file def load_env_vars [] { let env_file = "env" if ($env_file | path exists) { let content = open $env_file | lines | where ($it | str trim | str length) > 0 | where not ($it | str starts-with "#") | each {|line| if ($line | str contains "=") { let parts = ($line | split row "=") if ($parts | length) >= 2 { let key = ($parts | get 0 | str replace "export " "" | str trim) let raw_value = ($parts | get 1 | str trim) # Handle bash-style default values like ${VAR:-default} let value = if ($raw_value | str starts-with "${") and ($raw_value | str contains ":-") { # Extract default value from ${VAR:-default} let parts = ($raw_value | str replace "${" "" | str replace "}" "" | split row ":-") if ($parts | length) >= 2 { $parts | get 1 } else { $raw_value } } else { $raw_value } {$key: $value} } else { {} } } else { {} } } | reduce -f {} {|item, acc| if ($item | is-empty) { $acc } else { $acc | merge $item } } $content } else { { TARGET_PATH: "distribution", INSTALL_BIN_PATH: "/usr/local/bin", ARCHIVE_DIR_PATH: "/tmp", BIN_ARCHIVES_DIR_PATH: "bin_archives" } } } # Load build targets configuration def load_targets_config [] { if ("etc/build_targets.toml" | path exists) { open etc/build_targets.toml } else { {targets: {}} } } # Get host platform information def get_host_info [] { let arch = match (^uname -m) { "x86_64" => "amd64", "aarch64" => "arm64", "arm64" => "arm64", $arch if ($arch | str starts-with "arm") => "arm64", $other => $other } let platform = match (^uname -s | str downcase) { $os if ($os | str contains "linux") => "linux", $os if ($os | str contains "darwin") => "darwin", $os if ($os | str contains "windows") => "windows", $other => $other } {platform: $platform, arch: $arch, full: $"($platform)-($arch)"} } # Get all built plugin binaries for a specific target or all targets def get_built_plugins [target: string = ""] { let config = load_targets_config let host_info = get_host_info if ($target | str length) > 0 { # Special handling for "host" target or detected host platform if ($target == "host") or ($target == $host_info.full) { # Get plugins for host platform (native build) glob "nu_plugin_*/target/release/nu_plugin_*" | where ($it | path type) == "file" | where ($it | path exists) | where not ($it | str ends-with ".d") | each {|path| let plugin_name = $path | path basename let plugin_dir = $path | path dirname | path dirname | path dirname { name: $plugin_name, path: $path, source_dir: $plugin_dir, target: $host_info.full, size: (ls $path | get size | get 0) } } } else if ($target in ($config.targets | transpose name config | get name)) { # Get plugins for specific target let target_config = $config.targets | get $target get_plugins_for_target $target $target_config } else { print $"āŒ Unknown target: ($target)" [] } } else { # Get plugins for host platform - use actual detected platform glob "nu_plugin_*/target/release/nu_plugin_*" | where ($it | path type) == "file" | where ($it | path exists) | where not ($it | str ends-with ".d") | each {|path| let plugin_name = $path | path basename let plugin_dir = $path | path dirname | path dirname | path dirname { name: $plugin_name, path: $path, source_dir: $plugin_dir, target: $host_info.full, size: (ls $path | get 0.size) } } } } # Get plugins for a specific target def get_plugins_for_target [target_name: string, target_config: record] { let rust_target = $target_config.rust_target let binary_ext = $target_config | get binary_extension? | default "" # Look for binaries in target-specific directories let target_pattern = $"nu_plugin_*/target/($rust_target)/release/nu_plugin_*" glob $target_pattern | where ($it | path type) == "file" | where ($it | path exists) | where not ($it | str ends-with ".d") | each {|path| let plugin_name = $path | path basename let plugin_dir = $path | path dirname | path dirname | path dirname | path dirname { name: $plugin_name, path: $path, source_dir: $plugin_dir, target: $target_name, size: (ls $path | get 0.size) } } } # Get all available built targets def get_available_targets [] { let config = load_targets_config let host_info = get_host_info mut available = [] # Check host platform first let host_plugins = glob "nu_plugin_*/target/release/nu_plugin_*" | where not ($it | str ends-with ".d") if ($host_plugins | length) > 0 { $available = ($available | append { name: $host_info.full, platform_name: $host_info.full, count: ($host_plugins | length), description: "Host platform binaries" }) } # Check cross-compiled targets for target in ($config.targets | transpose name config) { let rust_target = $target.config.rust_target let target_plugins = glob $"nu_plugin_*/target/($rust_target)/release/nu_plugin_*" | where not ($it | str ends-with ".d") if ($target_plugins | length) > 0 { $available = ($available | append { name: $target.name, platform_name: $target.config.platform_name, count: ($target_plugins | length), description: $target.config.description }) } } $available } # Create target directory structure def create_target_structure [target_path: string] { if not ($target_path | path exists) { mkdir $target_path print $"šŸ“ Created target directory: ($target_path)" } # Create subdirectories if needed let subdirs = ["scripts", "docs"] for subdir in $subdirs { let subdir_path = $"($target_path)/($subdir)" if not ($subdir_path | path exists) { mkdir $subdir_path } } } # Copy plugin binaries to target def copy_plugins [plugins: list, target_path: string] { print $"šŸ“¦ Copying ($plugins | length) plugin binaries..." for plugin in $plugins { let dest_path = $"($target_path)/($plugin.name)" cp $plugin.path $dest_path # Make executable chmod +x $dest_path print $" āœ… ($plugin.name) (($plugin.size))" } } # Copy additional files def copy_additional_files [target_path: string] { let files_to_copy = [ {src: "LICENSE", dest: "LICENSE", required: false}, {src: "README.md", dest: "README", required: false}, {src: "env", dest: "env", required: false} ] for file in $files_to_copy { if ($file.src | path exists) { cp $file.src $"($target_path)/($file.dest)" print $" šŸ“„ Copied ($file.src) → ($file.dest)" } else if $file.required { print $" āš ļø Required file not found: ($file.src)" } } } # Create installation script def create_install_script [target_path: string, install_file: string] { let timestamp = (date now | format date "%Y-%m-%d %H:%M:%S") let install_script_content = "#!/usr/bin/env nu # Auto-generated installation script for nushell plugins # Generated at: " + $timestamp + " def main [ --bin-path (-b): string = \"/usr/local/bin\" # Installation path --plugins (-p): list = [] # Specific plugins to install --list (-l) # List available plugins --dry-run (-d) # Show what would be done --register-only (-r) # Only register plugins, don't copy binaries --no-register # Copy binaries but don't register with nushell --verify (-v) # Verify plugin registration after installation ] { let available_plugins = ls | where type == file and name =~ \"nu_plugin_\" | get name if " + '$list' + " { print \"šŸ“¦ Available plugins:\" for plugin in " + '$available_plugins' + " { print " + '$" - ($plugin)"' + " } return } let plugins_to_install = if (" + '$plugins' + " | length) > 0 { " + '$plugins' + " | where " + '$it' + " in " + '$available_plugins' + " } else { " + '$available_plugins' + " } if (" + '$plugins_to_install' + " | length) == 0 { print \"ā“ No plugins to install\" return } print " + '$"šŸš€ Processing ($plugins_to_install | length) plugins..."' + " # Step 1: Copy binaries (unless register-only mode) if not " + '$register_only' + " { print " + '$"\\nšŸ“¦ Installing binaries to ($bin_path)..."' + " for plugin in " + '$plugins_to_install' + " { if " + '$dry_run' + " { print " + '$"Would install: ($plugin) → ($bin_path)/($plugin)"' + " } else { try { cp " + '$plugin' + " " + '$"($bin_path)/($plugin)"' + " chmod +x " + '$"($bin_path)/($plugin)"' + " print " + '$" āœ… Installed ($plugin)"' + " } catch {|err| print " + '$" āŒ Failed to install ($plugin): ($err.msg)"' + " } } } } # Step 2: Register plugins with nushell (unless disabled) if not " + '$no_register' + " { print \"\\nšŸ”Œ Registering plugins with nushell...\" for plugin in " + '$plugins_to_install' + " { let plugin_path = if " + '$register_only' + " { # Use current directory path for register-only mode " + '$"./($plugin)"' + " } else { # Use installation path for normal mode " + '$"($bin_path)/($plugin)"' + " } if " + '$dry_run' + " { print " + '$"Would register: plugin add ($plugin_path)"' + " } else { try { nu -c " + '$"plugin add ($plugin_path)"' + " print " + '$" āœ… Registered ($plugin)"' + " } catch {|err| print " + '$" āš ļø Failed to register ($plugin): ($err.msg)"' + " print " + '$" You can manually register with: plugin add ($plugin_path)"' + " } } } } # Step 3: Verify plugin registration (if requested and plugins were registered) if " + '$verify' + " and not " + '$no_register' + " and not " + '$dry_run' + " { print \"\\nšŸ” Verifying plugin registration...\" for plugin in " + '$plugins_to_install' + " { let plugin_name = (" + '$plugin' + " | str replace \"nu_plugin_\" \"\") try { let found = (nu -c " + '$"plugin list | where name =~ ($plugin_name) | length"' + " | into int) if " + '$found' + " > 0 { print " + '$" āœ… Verified ($plugin)"' + " } else { print " + '$" āŒ Not found in plugin list: ($plugin)"' + " } } catch {|err| print " + '$" āš ļø Could not verify ($plugin): ($err.msg)"' + " } } } # Summary if not " + '$dry_run' + " { if " + '$register_only' + " { print \"\\nāœ… Plugin registration completed!\" if " + '$verify' + " { print \"šŸ’” Run 'plugin list' to see all registered plugins\" } } else if " + '$no_register' + " { print \"\\nāœ… Plugin installation completed!\" print \"šŸ’” To register plugins, run: nu install_nu_plugins.nu --register-only\" } else { print \"\\nāœ… Plugin installation and registration completed!\" if " + '$verify' + " { print \"šŸ’” All plugins verified. Run 'plugin list' to see details\" } else { print \"šŸ’” Restart nushell or run 'plugin list' to see registered plugins\" print \" Add --verify to automatically check registration next time\" } } } } " $install_script_content | save --force ($target_path | path join $install_file) chmod +x ($target_path | path join $install_file) print $"šŸ“œ Created installation script: ($install_file)" } # Main function def main [ --target-path (-t): string = "" # Override target path --platform (-p): string = "" # Specific platform to collect (e.g., linux-amd64) --all-platforms (-a) # Collect all available platforms --force (-f) # Force overwrite existing files --list (-l) # List available plugins only --list-platforms # List available platforms ] { # Ensure we're in the repository root directory if not ("nu_plugin_clipboard" | path exists) { error make {msg: "Please run this script from the nushell-plugins repository root directory"} } print "šŸ“¦ Nushell Plugin Collection & Installation Preparation" # Load environment variables let env_vars = load_env_vars let base_target_path = if ($target_path | str length) > 0 { $target_path } else { $env_vars.TARGET_PATH? | default "distribution" } let install_file = $env_vars.INSTALL_FILE? | default "install_nu_plugins.nu" # List platforms mode if $list_platforms { let available = get_available_targets if ($available | length) == 0 { print "ā“ No built platforms found" print "šŸ’” Run cross-compilation builds first" } else { print "šŸ“Š Available built platforms:" for target in $available { print $" āœ… ($target.name) ($target.platform_name): ($target.count) plugins" print $" ($target.description)" } } return } # Determine platforms to collect let available_targets = get_available_targets let host_info = get_host_info let platforms_to_collect = if $all_platforms { $available_targets } else if ($platform | str length) > 0 { # Handle "host" as a special case - map to actual platform let target_to_find = if ($platform == "host") { $host_info.full } else { $platform } let selected = $available_targets | where name == $target_to_find if ($selected | length) == 0 { print $"āŒ Platform not found: ($platform)" print $"šŸ’” Available platforms: ($available_targets | get name | str join ', ')" exit 1 } $selected } else { # Default to host platform if available let host_info = get_host_info let host_target = $available_targets | where name == $host_info.full if ($host_target | length) > 0 { $host_target } else { # Fallback to first available platform $available_targets | first 1 } } if ($platforms_to_collect | length) == 0 { print "ā“ No platforms to collect" print "šŸ’” Build plugins first with: just build or just build-cross-all" exit 1 } print $"šŸŽÆ Platforms to collect: ($platforms_to_collect | length)" for platform in $platforms_to_collect { print $" - ($platform.name): ($platform.count) plugins" } # Process each platform mut total_collected = 0 for platform_info in $platforms_to_collect { let platform_name = $platform_info.name let plugins = get_built_plugins $platform_name if $list { if ($plugins | length) == 0 { print $"ā“ No built plugins found for ($platform_name)" } else { print $"\nšŸ“¦ ($platform_name) - ($plugins | length) built plugins:" for plugin in $plugins { print $" āœ… ($plugin.name) (($plugin.size)) from ($plugin.source_dir)" } } continue } if ($plugins | length) == 0 { print $"āš ļø No plugins found for platform: ($platform_name)" continue } # Create platform-specific directory structure let platform_target_path = if $all_platforms { $"($base_target_path)/($platform_name)" } else { $base_target_path } print $"\nšŸ“¦ Collecting ($plugins | length) plugins for ($platform_name)..." print $"šŸ“ Target: ($platform_target_path)" # Check if target exists and handle accordingly if ($platform_target_path | path exists) and not $force { print $"āš ļø Target directory '($platform_target_path)' already exists" let confirm = input "Continue and overwrite? [y/N]: " if ($confirm | str downcase) != "y" { print "āŒ Skipping ($platform_name)" continue } } # Create target structure create_target_structure $platform_target_path # Copy plugins copy_plugins $plugins $platform_target_path # Copy additional files print "šŸ“„ Copying additional files..." copy_additional_files $platform_target_path # Create platform-specific installation script create_install_script $platform_target_path $install_file $total_collected = $total_collected + ($plugins | length) print $"āœ… Collected ($plugins | length) plugins for ($platform_name)" } # List mode summary if $list { return } # Summary print $"\nšŸŽ‰ Collection completed!" print $"šŸ“ Base directory: ($base_target_path)" print $"šŸ“¦ Total plugins collected: ($total_collected)" print $"šŸŽÆ Platforms: ($platforms_to_collect | get name | str join ', ')" print "\nšŸ’” Next steps:" if $all_platforms { print " 1. Package all platforms: just pack-cross" print " 2. Test individual platforms: cd distribution/PLATFORM && nu install_nu_plugins.nu --dry-run" } else { let final_path = if $all_platforms { $"($base_target_path)/($platforms_to_collect.0.name)" } else { $base_target_path } print $" 1. Test installation: cd ($final_path) && nu ($install_file) --dry-run" print $" 2. Package for distribution: just pack" print $" 3. Install locally: cd ($final_path) && nu ($install_file)" } } if ($env.NUSHELL_EXECUTION_CONTEXT? | default "" | str contains "run") { main }