nushell-plugins/scripts/create_full_distribution.nu
Jesús Pérez 2f6089caaf # Summary
feat: bootstrap installer with robust archive extraction, version mismatch handling, and improved PATH messaging

## Detailed Description

This commit 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.

## Major Changes (Session 2025-10-19)

### 0. Bootstrap Installer & Distribution Improvements (NEW)

#### 0a. Install Script Architecture - DRY Design

**Issue**: Code duplication across installation paths
- `./install.sh`, `./scripts/templates/install.sh`, `installers/bootstrap/install.sh` were separate copies
- Changes required updating multiple files
- Risk of divergence and inconsistency

**Solution**: Implemented symlink-based DRY architecture
- Single source of truth: `installers/bootstrap/install.sh` (1,247 lines)
- Symlinks created:
  - `./install.sh` → `installers/bootstrap/install.sh`
  - `./scripts/templates/install.sh` → `installers/bootstrap/install.sh`
- All changes automatically propagate through symlinks
- No code duplication

**Impact**:
-  Single maintenance point for all install scripts
-  Consistent behavior across all paths
-  Reduced maintenance burden
-  No divergence risk

#### 0b. Archive Extraction - Version-Agnostic Binary Detection

**Issue**: "No Nushell binary found in extracted archive"
- Script failed when extracting from `nushell-full-0.108.0-darwin-arm64.tar.gz`
- Error occurred even though binaries were present at `nushell-full-0.108.0/bin/nu`

**Root Cause**: `find` command returning parent directory itself in results
```bash
#  OLD - find returns parent directory first
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" | head -1)
# Returns: /tmp/nushell-install-22919/nushell-extract (parent!)
# Should return: /tmp/nushell-install-22919/nushell-extract/nushell-full-0.108.0 (subdirectory)
```

**Solution**: Added `-not -path` to exclude starting directory
```bash
#  NEW - exclude parent directory
local extracted=$(find "$extract_dir" -maxdepth 1 -type d -name "nushell-*" -not -path "$extract_dir" | head -1)
# Now correctly returns the subdirectory
```

**Additional Improvements**:
- 4-level fallback detection strategy:
  1. Check `$extracted/bin/nu` (subdirectory structure)
  2. Check `$extracted/nu` (flat structure)
  3. Fallback search for any `nushell-*` subdirectory with `bin/nu`
  4. Last resort recursive search for any executable named `nu`
- Validates binaries exist before using them
- Clear error reporting with all search locations

**Version-Agnostic**:
- Uses `nushell-*` pattern (not hardcoded version numbers)
- Works with any Nushell version: 0.107, 0.108, 0.109, etc.
- Supports both `.tar.gz` and `.zip` archive formats

**Impact**:
-  Archive extraction works reliably
-  Works with any Nushell version
-  Clear error messages guide users
-  Multiple archive structure support

#### 0c. Plugin Registration - Version Mismatch Handling

**Issue**: Plugin registration failed with version incompatibility errors
```
Error: nu:🐚:plugin_failed_to_load
× Plugin `polars` is compiled for nushell version 0.107.1, which is not compatible with version 0.108.0
```

**Solution**: Implemented intelligent error classification
- Captures both stdout and stderr from plugin add commands
- Detects version incompatibility: "is not compatible with version" or "is compiled for nushell version"
- Classifies errors into three categories:
  1. **Success**: Plugin registered successfully 
  2. **Incompatible**: Version mismatch (skipped gracefully) ⚠️
  3. **Failed**: Other registration failures 
- Reports summary with counts of each category
- Installation continues successfully even with version mismatches

**New Error Reporting**:
```
 Registered nu_plugin_auth
⚠️  Skipping nu_plugin_polars: Version mismatch (built for different Nushell version)
 Successfully registered 13 plugins
⚠️  Skipped 1 incompatible plugins (version mismatch):
  - nu_plugin_polars
```

**Impact**:
-  No installation failures due to version mismatches
-  Users informed of incompatible plugins
-  Clear distinction between error types
-  Installation completes successfully

#### 0d. Shell Configuration PATH Update - Clear Messaging

**Issue**: Confusing PATH update messages
- User sees: "PATH already updated" for all files
- Then sees: "No shell configuration files were updated" warning
- Then sees: "Please manually add to your PATH" error
- **Problem**: Contradictory messages when PATH is already configured everywhere

**Root Cause**: Script conflated two states
- State 1: "Was PATH found in files?" (skips updating if found)
- State 2: "Did we add PATH to any file?" (used for messaging)
- Both states ignored means no update was made, but PATH might already exist

**Solution**: Track two separate states
```bash
local updated=false        # Was PATH ADDED to any file?
local path_found=false     # Was PATH FOUND in any file?

# In loop:
if grep -q "$install_dir" "$config_file"; then
    path_found=true        # Found it! Mark as true
    continue
fi

# After loop:
if [ "$updated" = "true" ]; then
    log_success "Shell configuration updated"
elif [ "$path_found" = "true" ]; then
    log_success "PATH is already configured in your shell configuration files"
else
    log_warn "Could not find or update shell configuration"
fi
```

**New Clear Messages**:
-  "PATH is already configured in your shell configuration files" (when found everywhere)
-  "Shell configuration updated" (when just added)
- ⚠️ "Could not find or update shell configuration" (when file missing)

**Impact**:
-  Non-contradictory messages
-  Users understand what happened
-  No false warnings when PATH already configured
-  Clear guidance when manual action needed

#### 0e. Installation Features

**`--source-path` Option** (Local Installation):
- Install from local archive: `./install.sh --source-path archive.tar.gz`
- Install from local directory: `./install.sh --source-path /path/to/binaries`
- Default behavior: `./install.sh --source-path` uses `./bin_archives`
- Works with custom `--install-dir` paths
- No download needed, offline installation support

**`--uninstall` with Configuration Management**:
- Prompts user: "Remove ~/.config/nushell? [y/N]"
- Removes all installed binaries and plugins
- Preserves user choice for configuration
- Clean uninstall before fresh reinstall

#### 0f. Documentation Updates

**CLAUDE.md**:
- Added "Install Script Architecture (DRY Design)" section
- Documents source of truth and symlink structure
- Explains `--source-path` feature
- Shows version-agnostic archive detection
- Lists DRY architecture benefits

**README.md**:
- Added "Install Script Architecture (DRY Design)" subsection
- Shows symlink structure with arrows
- Provides `--source-path` usage examples
- Explains version-agnostic detection
- "How DRY Works" 3-step explanation

#### 0g. Files Modified

Core Changes:
- `installers/bootstrap/install.sh` (+3 lines for PATH messaging fix)
  - Archive extraction fix with `-not -path`
  - Plugin registration error classification
  - Clear PATH update messaging
  - Total: 1,247 lines (unified)

Auto-Updated via Symlinks:
- `./install.sh` - Auto-updated (1,247 lines)
- `./scripts/templates/install.sh` - Auto-updated (1,247 lines)

Documentation:
- `CLAUDE.md` - Added install architecture section
- `README.md` - Added install architecture subsection
- `CHANGELOG.md` - Added comprehensive entry (+100 lines)

#### 0h. Testing & Verification

All scenarios tested and verified:
- [x] Archive extraction works with version-agnostic detection
- [x] Installation to `~/.local` successful (16 binaries)
- [x] Installation to `~/.local/bin` successful (21 plugins loaded)
- [x] Plugin registration handles version mismatches gracefully
- [x] PATH messaging is clear and non-contradictory
- [x] Clean uninstall followed by fresh reinstall works perfectly

#### 0i. Impact

User-Facing Benefits:
-  Users can install from any version of nushell-full archive
-  Version mismatch plugins skipped without breaking installation
-  Clear, honest error messages
-  Non-confusing PATH update messages
-  Offline installation support via `--source-path`
-  Clean uninstall/reinstall workflow

Developer Benefits:
-  Single source of truth eliminates code duplication
-  Changes propagate automatically through symlinks
-  Reduced maintenance burden
-  Consistent behavior across all paths
-  Production-ready installation process

---

### 1. Help System Integration (New Feature)

**Issue**: Version-update module recipes were not discoverable
- Not shown in `just help modules`
- Not referenced in `just help`
- Not included in help navigation system
- Users had to manually run `just --list` to find update commands

**Solution**:
- Added version-update module to all help outputs
- Updated `justfiles/help.just` to document all 30+ version-update recipes
- Created new `just commands` recipe as discoverable alias for `just --list`
- Integrated version-update into help-all workflow

**Impact**:
- Version-update commands now fully discoverable via help system
- Users can find update commands with: `just help modules`, `just help`, `just commands`
- Improved overall help system navigation

**Files Modified**:
- `justfiles/help.just` (+23 lines)
  - Added version-update module to help sections
  - Added to modules list
  - Added to help-all workflow
  - New `commands` recipe showing all recipes by group

### 2. Build Process Fixes (Phase 3: Bin Archives)

#### 2a. Plugin Archive Collection Bug

**Issue**: "No plugins found to package" warning in Phase 3
- Collected 26 plugin binaries but reported 0
- Archive creation skipped because count was wrong

**Root Cause**: `each` command returns null, so `| length` returned 0
```nushell
#  OLD - each returns null
let plugin_count = (ls nu_plugin_*/target/release/nu_plugin_* | each {|p|
    cp $p.name $"($temp_dir)/"
} | length)  # Returns 0!
```

**Solution**: Separated counting from copying with proper filtering
```nushell
#  NEW - count before operations
let plugins_to_copy = (ls nu_plugin_*/target/release/nu_plugin_* | where type == "file")
let plugin_count = ($plugins_to_copy | length)
```

**Impact**:
- Now correctly collects and reports 26 plugins
- Filters out .d dependency files automatically
- Warning eliminated

#### 2b. Tar Archive Path Handling

**Issue**: Tar command failing silently with relative paths in subshell
- `cd $temp_dir` changes context unpredictably
- Relative path `../$archive_name` fails in subshell
- Archive file not created despite exit code 0

**Root Cause**: Shell context and relative path issues in Nushell `do` block

**Solution**: Used `tar -C` with absolute paths instead of `cd`
```nushell
#  OLD - unreliable context switching
do {
    cd $temp_dir
    tar -czf ../$archive_name .
}

#  NEW - absolute paths, no context switching
tar -C $temp_dir -czf $archive_path .
```

**Additional Improvements**:
- Absolute path construction using `pwd | path join`
- Better error diagnostics with exit code and stderr output
- File verification after creation

**Impact**:
- Tar archives now created successfully
- Robust path handling across platforms
- Clear error messages for debugging

#### 2c. File Size Calculation Type Error

**Issue**: Runtime error when calculating archive size
```
Error: The '/' operator does not work on values of type 'list<filesize>'
```

**Root Cause**: `ls` returns list of records, so `.size` was a list
```nushell
#  OLD - returns list<filesize>
(ls $archive_path).size / 1024 / 1024

#  NEW - returns filesize
(ls $archive_path | get 0.size) / 1024 / 1024
```

**Impact**:
- Proper file size calculation in MB
- No more type errors

**Files Modified**:
- `scripts/create_full_distribution.nu` (+58 lines, refactored plugin collection)
  - Fixed plugin counting logic
  - Improved path handling with absolute paths
  - Enhanced error diagnostics

### 3. Plugin Rebuild Optimization

**Issue**: All plugins marked for rebuild even when dependencies unchanged
- Step 4 (`update_all_plugins.nu`) touched all Cargo.toml files at 01:00:32
- Step 5 saw all files as "newer" than binaries
- Marked ALL plugins for rebuild, though cargo only rebuilt changed ones

**Root Cause**: Script always saved files, even when no changes made
```nushell
#  OLD - always saves, touching file timestamp
$updated_content | to toml | save -f $cargo_toml
```

**Solution**: Only save if content actually changed
```nushell
#  NEW - compare before writing
let original_toml = $content | to toml
let new_toml = $updated_content | to toml

if $original_toml != $new_toml {
    $updated_content | to toml | save -f $cargo_toml
}
```

**Impact**:
- Unchanged files preserve original timestamps
- Only plugins with actual dependency changes are rebuilt
- Efficient rebuild process with accurate file modification detection

**Files Modified**:
- `scripts/update_all_plugins.nu` (+12 lines, added content comparison)
  - Only touches files with real changes
  - Preserves timestamps for efficiency
  - Clearer logic and comments

### 4. Documentation

**Files Modified**:
- `CHANGELOG.md` (+56 lines)
  - Added comprehensive 2025-10-19 entry
  - Documented all fixes with root causes
  - Listed files modified and impact summary

## Technical Details

### Nushell Patterns Used

1. **Proper List Handling**:
   - `ls` returns list of records, access with `| get 0.size`
   - Filter with `where type == "file"` to exclude metadata

2. **Absolute Path Construction**:
   - `pwd | append "path" | path join` for cross-platform paths
   - Safer than string concatenation with `/`

3. **Content Comparison**:
   - Compare TOML string representation before saving
   - Preserves file timestamps for efficiency

4. **Error Diagnostics**:
   - Capture `stderr` from commands
   - Report exit codes and error messages separately

## Testing

- [x] Help system shows version-update module
- [x] `just commands` displays all recipes by group
- [x] Phase 3 bin archive creation works
- [x] Plugin collection reports correct count (26)
- [x] Tar archives created successfully
- [x] File size calculated correctly
- [x] Plugin rebuild only touches changed files
- [x] CHANGELOG updated with all changes

## Files Changed

```
38 files changed, 2721 insertions(+), 2548 deletions(-)

Core Changes:
- justfiles/help.just                  (+23)  Help system integration
- scripts/create_full_distribution.nu  (+58)  Build process fixes
- scripts/update_all_plugins.nu        (+12)  Rebuild optimization
- CHANGELOG.md                         (+56)  Documentation

Dependency Updates:
- All plugin Cargo.toml and Cargo.lock files (version consistency)
```

## Breaking Changes

None. These are bug fixes and optimizations that maintain backward compatibility.

## Migration Notes

No migration needed. Improvements are transparent to users.

## Related Issues

- Help system discoverability
- Build process Phase 3 failures
- Unnecessary plugin rebuilds
- Build process reliability

## Checklist

- [x] Changes follow Rust/Nushell idioms
- [x] Code is well-commented
- [x] Error handling is comprehensive
- [x] Documentation is updated
- [x] All changes tested
- [x] No breaking changes introduced
2025-10-19 02:39:31 +01:00

550 lines
19 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
] {
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
log_info $"📦 Collecting plugins for ($platform)..."
# Get list of plugin binaries (exclude .d dependency files and other metadata)
# Only include executable binaries: nu_plugin_name (no extension on Unix, .exe on Windows)
let plugins_to_copy = (
try {
ls nu_plugin_*/target/release/nu_plugin_*
| where type == "file"
| where ($it.name | str ends-with ".d") == false # Exclude .d dependency files
| where ($it.name | regex match "nu_plugin_[a-z_]+$") != null # Match base plugin names only
} catch {
[]
}
)
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. Test installation:"
# Show example for first built platform
let first_platform = $platforms | first 1 | get 0
log_info $" cd distribution/($first_platform) && ./install.nu --verify"
}
} 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"
}
}
}