1102 lines
30 KiB
Plaintext
Raw Permalink Normal View History

feat: Add complete Nushell full distribution system ## Major Features Added - **Complete distribution infrastructure**: Build, package, and distribute Nushell binary alongside plugins - **Zero-prerequisite installation**: Bootstrap installers work on fresh systems without Rust/Cargo/Nushell - **Cross-platform support**: Linux, macOS, Windows (x86_64, ARM64) - **Self-contained packages**: Everything needed for complete Nushell environment ## New Components ### Build System - `scripts/build_nushell.nu` - Build nushell with all workspace plugins - `scripts/collect_full_binaries.nu` - Collect nu binary + all plugins - `justfiles/full_distro.just` - 40+ new recipes for distribution workflows ### Bootstrap Installers (Zero Prerequisites) - `installers/bootstrap/install.sh` - Universal POSIX installer (900+ lines) - `installers/bootstrap/install.ps1` - Windows PowerShell installer (800+ lines) - Complete platform detection, PATH setup, plugin registration ### Distribution System - `scripts/create_distribution_packages.nu` - Multi-platform package creator - `scripts/install_full_nushell.nu` - Advanced nu-based installer - `scripts/verify_installation.nu` - Installation verification suite - `scripts/lib/common_lib.nu` - Shared utilities and logging ### Configuration Management - `scripts/templates/default_config.nu` - Complete nushell configuration (500+ lines) - `scripts/templates/default_env.nu` - Cross-platform environment setup - `etc/distribution_config.toml` - Central distribution settings - `scripts/templates/uninstall.sh` & `uninstall.ps1` - Clean removal ## Key Workflows ```bash just build-full # Build nushell + all plugins just pack-full-all # Create packages for all platforms just verify-full # Verify installation just release-full-cross # Complete cross-platform release ``` ## Installation Experience - One-liner: `curl -sSf https://your-url/install.sh | sh` - Multiple modes: user, system, portable installation - Automatic plugin registration with verification - Professional uninstall capability ## Benefits - ✅ Solves bootstrap problem - no prerequisites needed - ✅ Production-ready distribution system - ✅ Complete cross-platform support - ✅ Professional installation experience - ✅ Integrates seamlessly with existing plugin workflows - ✅ Enterprise-grade verification and logging
2025-09-24 18:52:07 +01:00
# Nushell Configuration Template
# Default configuration for Nushell + Plugins distribution
# Created by nushell-plugins full distribution system
# =============================================================================
# CORE CONFIGURATION
# =============================================================================
$env.config = {
# Show banner on startup
show_banner: false
# Editing mode (emacs, vi)
edit_mode: emacs
# Shell integration features
shell_integration: true
# Use ansi coloring
use_ansi_coloring: true
# Bracketed paste mode
bracketed_paste: true
# Render right prompt on the last line of the prompt
render_right_prompt_on_last_line: false
# =============================================================================
# TABLE CONFIGURATION
# =============================================================================
table: {
# Table rendering mode
mode: rounded # basic, compact, compact_double, light, thin, with_love, rounded, reinforced, heavy, none, other
# Index column mode
index_mode: always # always, never, auto
# Show empty tables
show_empty: true
# Table padding
padding: { left: 1, right: 1 }
# Trim table content
trim: {
methodology: wrapping # wrapping, truncating
wrapping_try_keep_words: true
truncating_suffix: "..."
}
# Header styling
header_on_separator: false
}
# =============================================================================
# ERROR HANDLING
# =============================================================================
error_style: "fancy" # fancy, plain
# =============================================================================
# DATETIME FORMATTING
# =============================================================================
datetime_format: {
# Normal datetime format
normal: '%a, %d %b %Y %H:%M:%S %z' # Tue, 1 Jan 2023 12:00:00 +0000
# Table datetime format (shorter for table display)
table: '%m/%d/%y %I:%M:%S%p' # 01/01/23 12:00:00AM
}
# =============================================================================
# COMPLETIONS CONFIGURATION
# =============================================================================
completions: {
# Case sensitivity for completions
case_sensitive: false
# Quick completions (don't wait for all to be calculated)
quick: true
# Partial completions (complete common prefix)
partial: true
# Completion algorithm
algorithm: "prefix" # prefix, fuzzy
# External completer command
external: {
enable: true
max_results: 100
completer: null
}
}
# =============================================================================
# FILE SIZE FORMATTING
# =============================================================================
filesize: {
# Use metric units (KB, MB, GB) vs binary (KiB, MiB, GiB)
metric: false
# Filesize format
format: "auto" # auto, b, kb, kib, mb, mib, gb, gib, tb, tib, pb, pib, eb, eib, zb, zib
}
# =============================================================================
# CURSOR SHAPE CONFIGURATION
# =============================================================================
cursor_shape: {
# Cursor shapes for different modes
emacs: line # line, block, underscore, blink_line, blink_block, blink_underscore
vi_insert: block
vi_normal: underscore
}
# =============================================================================
# COLOR CONFIGURATION
# =============================================================================
color_config: {
# Separator color
separator: white
# Leading trailing space
leading_trailing_space_bg: { attr: n }
# Header colors
header: green_bold
# Empty cell
empty: blue
# Boolean values
bool: {
true: light_cyan
false: light_gray
}
# Integer types
int: white
float: white
# String and date types
string: white
date: purple
# Duration
duration: white
# File sizes
filesize: {
b: white
kb: cyan
mb: blue
gb: green
tb: red
pb: purple
}
# Range
range: white
# Binary
binary: cyan_bold
# Lists
list: white
record: white
# Nothing
nothing: white
# Block
block: white
# Hints
hints: dark_gray
# Search result
search_result: {
fg: white
bg: red
}
# Shape colors
shape_garbage: { fg: white bg: red attr: b}
shape_binary: purple_bold
shape_bool: light_cyan
shape_int: purple_bold
shape_float: purple_bold
shape_range: yellow_bold
shape_internalcall: cyan_bold
shape_external: cyan
shape_externalarg: green_bold
shape_literal: blue
shape_operator: yellow
shape_signature: green_bold
shape_string: green
shape_string_interpolation: cyan_bold
shape_datetime: cyan_bold
shape_list: cyan_bold
shape_table: blue_bold
shape_record: cyan_bold
shape_block: blue_bold
shape_filepath: cyan
shape_directory: cyan
shape_globpattern: cyan_bold
shape_variable: purple
shape_flag: blue_bold
shape_custom: green
shape_nothing: light_cyan
shape_matching_brackets: { attr: u }
}
# =============================================================================
# HISTORY CONFIGURATION
# =============================================================================
history: {
# Maximum number of history entries
max_size: 100_000
# Sync history on enter
sync_on_enter: true
# History file format
file_format: "plaintext" # sqlite, plaintext
# Isolation mode
isolation: false
}
# =============================================================================
# KEYBINDINGS
# =============================================================================
keybindings: [
{
name: completion_menu
modifier: none
keycode: tab
mode: [emacs vi_normal vi_insert]
event: {
until: [
{ send: menu name: completion_menu }
{ send: menunext }
{ edit: complete }
]
}
}
{
name: history_menu
modifier: control
keycode: char_r
mode: [emacs, vi_insert, vi_normal]
event: { send: menu name: history_menu }
}
{
name: help_menu
modifier: none
keycode: f1
mode: [emacs, vi_insert, vi_normal]
event: { send: menu name: help_menu }
}
{
name: completion_previous
modifier: shift
keycode: backtab
mode: [emacs, vi_normal, vi_insert]
event: { send: menuprevious }
}
{
name: next_page
modifier: control
keycode: char_x
mode: emacs
event: { send: menupagenext }
}
{
name: undo_or_previous_page
modifier: control
keycode: char_z
mode: emacs
event: {
until: [
{ send: menupageprevious }
{ edit: undo }
]
}
}
{
name: escape
modifier: none
keycode: escape
mode: [emacs, vi_insert]
event: { send: esc }
}
{
name: cancel_command
modifier: control
keycode: char_c
mode: [emacs, vi_insert, vi_normal]
event: { send: ctrlc }
}
{
name: quit_shell
modifier: control
keycode: char_d
mode: [emacs, vi_insert, vi_normal]
event: { send: ctrld }
}
{
name: clear_screen
modifier: control
keycode: char_l
mode: [emacs, vi_insert, vi_normal]
event: { send: clearscreen }
}
{
name: search_history
modifier: control
keycode: char_q
mode: [emacs, vi_insert, vi_normal]
event: { send: searchhistory }
}
{
name: open_command_editor
modifier: control
keycode: char_o
mode: [emacs, vi_insert, vi_normal]
event: { send: openeditor }
}
{
name: move_up
modifier: none
keycode: up
mode: [emacs, vi_insert, vi_normal]
event: {
until: [
{ send: menuup }
{ send: up }
]
}
}
{
name: move_down
modifier: none
keycode: down
mode: [emacs, vi_insert, vi_normal]
event: {
until: [
{ send: menudown }
{ send: down }
]
}
}
{
name: move_left
modifier: none
keycode: left
mode: [emacs, vi_insert, vi_normal]
event: {
until: [
{ send: menuleft }
{ send: left }
]
}
}
{
name: move_right_or_take_history_hint
modifier: none
keycode: right
mode: [emacs, vi_insert, vi_normal]
event: {
until: [
{ send: historyhintcomplete }
{ send: menuright }
{ send: right }
]
}
}
{
name: move_one_word_left
modifier: control
keycode: left
mode: [emacs, vi_insert, vi_normal]
event: { edit: movewordleft }
}
{
name: move_one_word_right_or_take_history_hint
modifier: control
keycode: right
mode: [emacs, vi_insert, vi_normal]
event: {
until: [
{ send: historyhintwordcomplete }
{ edit: movewordright }
]
}
}
{
name: move_to_line_start
modifier: none
keycode: home
mode: [emacs, vi_insert, vi_normal]
event: { edit: movetolinestart }
}
{
name: move_to_line_start_alt
modifier: control
keycode: char_a
mode: [emacs, vi_insert]
event: { edit: movetolinestart }
}
{
name: move_to_line_end_or_take_history_hint
modifier: none
keycode: end
mode: [emacs, vi_insert, vi_normal]
event: {
until: [
{ send: historyhintcomplete }
{ edit: movetolineend }
]
}
}
{
name: move_to_line_end_alt
modifier: control
keycode: char_e
mode: [emacs, vi_insert]
event: {
until: [
{ send: historyhintcomplete }
{ edit: movetolineend }
]
}
}
{
name: move_to_line_start_vi
modifier: none
keycode: char_0
mode: vi_normal
event: { edit: movetolinestart }
}
{
name: move_to_line_end_vi
modifier: none
keycode: char_4
mode: vi_normal
event: { edit: movetolineend }
}
{
name: move_up_vi
modifier: none
keycode: char_k
mode: vi_normal
event: { send: up }
}
{
name: move_down_vi
modifier: none
keycode: char_j
mode: vi_normal
event: { send: down }
}
{
name: backspace
modifier: none
keycode: backspace
mode: [emacs, vi_insert]
event: { edit: backspace }
}
{
name: delete_char
modifier: none
keycode: delete
mode: [emacs, vi_insert]
event: { edit: delete }
}
{
name: delete_word
modifier: control
keycode: char_w
mode: [emacs, vi_insert]
event: { edit: backspaceword }
}
{
name: cut_word_to_right
modifier: control
keycode: char_k
mode: emacs
event: { edit: cuttoend }
}
{
name: cut_line_to_right
modifier: control
keycode: char_k
mode: vi_insert
event: { edit: cuttoend }
}
{
name: cut_line_from_start
modifier: control
keycode: char_u
mode: [emacs, vi_insert]
event: { edit: cutfromstart }
}
{
name: swap_graphemes
modifier: control
keycode: char_t
mode: emacs
event: { edit: swapgraphemes }
}
{
name: move_one_word_left_vi
modifier: none
keycode: char_b
mode: vi_normal
event: { edit: movewordleft }
}
{
name: move_one_word_right_vi
modifier: none
keycode: char_w
mode: vi_normal
event: { edit: movewordright }
}
{
name: move_one_word_right_end_vi
modifier: none
keycode: char_e
mode: vi_normal
event: { edit: movewordrightend }
}
{
name: move_one_big_word_left_vi
modifier: shift
keycode: char_b
mode: vi_normal
event: { edit: movebigwordleft }
}
{
name: move_one_big_word_right_vi
modifier: shift
keycode: char_w
mode: vi_normal
event: { edit: movebigwordright }
}
{
name: move_one_big_word_right_end_vi
modifier: shift
keycode: char_e
mode: vi_normal
event: { edit: movebigwordrightend }
}
{
name: delete_one_character_backward_vi
modifier: none
keycode: char_x
mode: vi_normal
event: { edit: backspace }
}
{
name: delete_one_word_backward_vi
modifier: none
keycode: char_X
mode: vi_normal
event: { edit: backspaceword }
}
{
name: delete_to_end_of_line_vi
modifier: shift
keycode: char_d
mode: vi_normal
event: { edit: cuttoend }
}
{
name: delete_line_vi
modifier: none
keycode: char_dd
mode: vi_normal
event: { edit: cutline }
}
{
name: change_to_end_of_line_vi
modifier: shift
keycode: char_c
mode: vi_normal
event: [
{ edit: cuttoend }
{ edit: insertmode }
]
}
{
name: change_line_vi
modifier: none
keycode: char_cc
mode: vi_normal
event: [
{ edit: cutline }
{ edit: insertmode }
]
}
{
name: open_line_above_vi
modifier: shift
keycode: char_o
mode: vi_normal
event: [
{ edit: insertcharafter }
{ edit: newline }
{ edit: moveup }
{ edit: insertmode }
]
}
{
name: open_line_below_vi
modifier: none
keycode: char_o
mode: vi_normal
event: [
{ edit: insertcharafter }
{ edit: newline }
{ edit: insertmode }
]
}
{
name: enter_vi_insert_mode
modifier: none
keycode: char_i
mode: vi_normal
event: { edit: insertmode }
}
{
name: enter_vi_append_mode
modifier: none
keycode: char_a
mode: vi_normal
event: [
{ edit: moveright }
{ edit: insertmode }
]
}
{
name: enter_vi_append_mode_at_end_of_line
modifier: shift
keycode: char_a
mode: vi_normal
event: [
{ edit: movetolineend }
{ edit: insertmode }
]
}
{
name: enter_vi_insert_mode_at_start_of_line
modifier: shift
keycode: char_i
mode: vi_normal
event: [
{ edit: movetolinestart }
{ edit: insertmode }
]
}
{
name: yank_vi
modifier: none
keycode: char_y
mode: vi_normal
event: { edit: yank }
}
{
name: yank_to_end_of_line_vi
modifier: shift
keycode: char_y
mode: vi_normal
event: { edit: yanktoend }
}
{
name: yank_line_vi
modifier: none
keycode: char_yy
mode: vi_normal
event: { edit: yankline }
}
{
name: paste_after_vi
modifier: none
keycode: char_p
mode: vi_normal
event: { edit: pasteafter }
}
{
name: paste_before_vi
modifier: shift
keycode: char_p
mode: vi_normal
event: { edit: pastebefore }
}
{
name: undo_vi
modifier: none
keycode: char_u
mode: vi_normal
event: { edit: undo }
}
{
name: redo_vi
modifier: control
keycode: char_r
mode: vi_normal
event: { edit: redo }
}
{
name: enter_vi_normal_mode
modifier: none
keycode: escape
mode: vi_insert
event: { edit: normalmode }
}
]
# =============================================================================
# MENU CONFIGURATION
# =============================================================================
menus: [
# Completion menu
{
name: completion_menu
only_buffer_difference: false
marker: "| "
type: {
layout: columnar
columns: 4
col_width: 20
col_padding: 2
}
style: {
text: green
selected_text: { attr: r }
description_text: yellow
match_text: { attr: u }
selected_match_text: { attr: ur }
}
}
# History menu
{
name: history_menu
only_buffer_difference: true
marker: "? "
type: {
layout: list
page_size: 10
}
style: {
text: green
selected_text: green_reverse
description_text: yellow
}
}
# Help menu
{
name: help_menu
only_buffer_difference: true
marker: "? "
type: {
layout: description
columns: 4
col_width: 20
col_padding: 2
selection_rows: 4
description_rows: 10
}
style: {
text: green
selected_text: green_reverse
description_text: yellow
}
}
]
# =============================================================================
# HOOKS CONFIGURATION
# =============================================================================
hooks: {
pre_prompt: [
# Update terminal title with current directory
{||
print -n $"\e]0;($env.PWD | path basename) - Nushell\e\\"
}
]
pre_execution: [
# Add timestamp to long-running commands
{||
let start_time = (date now | format date '%Y-%m-%d %H:%M:%S')
$env.COMMAND_START_TIME = $start_time
}
]
env_change: {
PWD: [
# Auto-activate direnv if available
{|before, after|
if (which direnv | is-not-empty) {
direnv export json | from json | default {} | load-env
}
}
]
}
command_not_found: {
# Suggest alternatives for command not found
|cmd_name| (
try {
if (which thefuck | is-not-empty) {
$"💡 Did you mean: (thefuck --alias | str trim)"
} else {
$"❌ Command not found: ($cmd_name)"
}
} catch {
$"❌ Command not found: ($cmd_name)"
}
)
}
display_output: {
# Custom output formatting
|output| (
if ($output | describe) == "table" {
$output | table --expand
} else {
$output
}
)
}
}
# =============================================================================
# PLUGIN CONFIGURATION
# =============================================================================
plugins: {
# Plugin loading timeout (in seconds)
timeout: 60
# Auto-register plugins on startup
auto_register: true
# Plugin search paths
search_paths: [
($nu.config-path | path dirname | path join "plugins")
]
}
# =============================================================================
# PERFORMANCE SETTINGS
# =============================================================================
# Buffer size for commands
buffer_editor: null
# Use LSP for completions if available
use_lsp: true
# Maximum recursion depth
recursion_limit: 50
# Plugin call timeout
plugin_timeout: 60_000 # milliseconds
# =============================================================================
# EXTERNAL INTEGRATIONS
# =============================================================================
# Carapace (external completer)
carapace: {
enable: false
cache_dir: ($env.HOME | path join ".cache" "carapace")
}
# Starship prompt
starship: {
enable: (which starship | is-not-empty)
config: ($nu.config-path | path dirname | path join "starship.toml")
}
# Zoxide integration
zoxide: {
enable: (which zoxide | is-not-empty)
hook: "pwd" # none, prompt, pwd
}
# =============================================================================
# DEVELOPMENT AND DEBUG
# =============================================================================
# Debug mode
debug_mode: false
# Log level
log_level: "warn" # error, warn, info, debug, trace
# Profiling
profile: false
}
# =============================================================================
# PLUGIN LOADING AND REGISTRATION
# =============================================================================
# Auto-load common plugins if they're available
let plugin_binaries = [
"nu_plugin_clipboard"
"nu_plugin_desktop_notifications"
"nu_plugin_hashes"
"nu_plugin_highlight"
"nu_plugin_image"
"nu_plugin_kcl"
"nu_plugin_tera"
"nu_plugin_custom_values"
"nu_plugin_example"
"nu_plugin_formats"
"nu_plugin_gstat"
"nu_plugin_inc"
"nu_plugin_polars"
"nu_plugin_query"
]
# Register plugins that are available
for plugin in $plugin_binaries {
try {
# Check if plugin is already registered
if (plugin list | where name == $plugin | is-empty) {
# Try to find and register the plugin
let plugin_path = (which $plugin | get path.0?)
if ($plugin_path | is-not-empty) {
plugin add $plugin_path
}
}
} catch {
# Silently ignore plugin loading errors
}
}
# =============================================================================
# CUSTOM FUNCTIONS AND ALIASES
# =============================================================================
# Enhanced ls with better colors and formatting
def --env la [...rest] {
ls -la $rest | sort-by type name
}
# Quick directory navigation
def --env .. [] {
cd ..
}
def --env ... [] {
cd ../..
}
def --env .... [] {
cd ../../..
}
# Git shortcuts (if git is available)
if (which git | is-not-empty) {
def gs [] { git status --short }
def ga [...files] { git add $files }
def gc [message: string] { git commit -m $message }
def gp [] { git push }
def gl [] { git log --oneline -10 }
}
# System information
def sysinfo [] {
{
OS: $nu.os-info.name
Arch: $nu.os-info.arch
Version: $nu.os-info.version
Kernel: $nu.os-info.kernel_version
Hostname: (hostname)
Shell: $"Nushell (version)"
Plugins: (plugin list | length)
Config: $nu.config-path
}
}
# Plugin management helpers
def plugin-info [] {
plugin list | select name version is_running pid
}
def plugin-restart [name: string] {
plugin stop $name
plugin use $name
}
# Quick file operations
def --env take [name: string] {
mkdir $name
cd $name
}
def trash [path: string] {
if (which trash | is-not-empty) {
^trash $path
} else {
print $"⚠️ No trash command available, use 'rm ($path)' to delete permanently"
}
}
# Enhanced which command
def which-all [command: string] {
which -a $command | select path
}
# =============================================================================
# PROMPT CONFIGURATION
# =============================================================================
# Custom prompt function (used if starship is not available)
def create_left_prompt [] {
let home = $nu.home-path
let dir = ([
($env.PWD | str substring 0..($home | str length) | str replace $home "~")
($env.PWD | str substring ($home | str length)..)
] | str join)
let path_color = (if (is-admin) { ansi red_bold } else { ansi green_bold })
let separator_color = (ansi light_gray)
let path_segment = $"($path_color)($dir)"
# Git information if in a git repo
let git_info = if (ls -la | where name == .git | is-not-empty) {
try {
let branch = (git branch --show-current 2>/dev/null | str trim)
if not ($branch | is-empty) {
let git_color = ansi yellow
$" ($git_color)($branch)(ansi reset)"
} else { "" }
} catch { "" }
} else { "" }
$"($path_segment)($git_info)($separator_color) (ansi reset)"
}
def create_right_prompt [] {
# Show current time and execution duration
let time_segment = (date now | format date '%H:%M:%S')
let duration = if "COMMAND_START_TIME" in $env {
let start = ($env.COMMAND_START_TIME | into datetime)
let duration = ((date now) - $start)
if ($duration > 1sec) {
$" took (ansi yellow)($duration)(ansi reset)"
} else { "" }
} else { "" }
$"(ansi light_gray)($time_segment)($duration)(ansi reset)"
}
# Set prompt functions if starship is not available
if not (which starship | is-not-empty) {
$env.PROMPT_COMMAND = { || create_left_prompt }
$env.PROMPT_COMMAND_RIGHT = { || create_right_prompt }
}
# =============================================================================
# STARTUP MESSAGE
# =============================================================================
# Uncomment to show welcome message
# print $"🚀 Welcome to Nushell + Plugins!"
# print $"📦 (plugin list | length) plugins loaded"
# print $"💡 Type 'help' for commands or 'sysinfo' for system information"
# print ""
# =============================================================================
# USER CUSTOMIZATION
# =============================================================================
# Load user-specific configuration if it exists
let user_config_file = ($nu.config-path | path dirname | path join "user_config.nu")
if ($user_config_file | path exists) {
source $user_config_file
}
# Load local project configuration if it exists
let local_config_file = (pwd | path join ".nu_config")
if ($local_config_file | path exists) {
source $local_config_file
}