
- Modularize justfile system with dedicated modules: * alias.just: Command aliases (h, b, c, s) * build.just: Build and cross-compilation commands * distro.just: Collection and packaging commands * help.just: Comprehensive help system with areas * qa.just: Testing and quality assurance * tools.just: Development tools and utilities * upstream.just: Repository tracking and sync - Fix platform detection in collect script: * Use actual platform names (darwin-arm64) instead of generic "host" * Support both "host" argument and auto-detection * Filter out .d dependency files from distribution - Fix packaging script issues: * Correct uname command syntax (^uname -m) * Fix string interpolation and environment parsing * Include plugin binaries in archives (was only packaging metadata) * Use proper path join instead of string interpolation * Add --force flag to avoid interactive prompts - Fix justfile absolute paths: * Replace relative paths with {{justfile_directory()}} function * Enable commands to work from any directory 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
476 lines
12 KiB
Bash
Executable File
476 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Universal Installer for Nushell Plugins
|
|
# Downloads and installs the latest release of nushell plugins for your platform
|
|
|
|
set -e
|
|
|
|
# Configuration
|
|
REPO_OWNER="YOUR_ORG" # Replace with actual GitHub org/user
|
|
REPO_NAME="nushell-plugins"
|
|
GITHUB_REPO="${REPO_OWNER}/${REPO_NAME}"
|
|
BASE_URL="https://github.com/${GITHUB_REPO}"
|
|
API_URL="https://api.github.com/repos/${GITHUB_REPO}"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Default values
|
|
INSTALL_DIR="/usr/local/bin"
|
|
VERSION=""
|
|
PLATFORM=""
|
|
FORCE=false
|
|
DRY_RUN=false
|
|
QUIET=false
|
|
TEMP_DIR=""
|
|
|
|
# Functions
|
|
log() {
|
|
if [ "$QUIET" != "true" ]; then
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
fi
|
|
}
|
|
|
|
warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1" >&2
|
|
}
|
|
|
|
error() {
|
|
echo -e "${RED}[ERROR]${NC} $1" >&2
|
|
}
|
|
|
|
success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
usage() {
|
|
cat << EOF
|
|
Universal Installer for Nushell Plugins
|
|
|
|
USAGE:
|
|
$0 [OPTIONS]
|
|
|
|
OPTIONS:
|
|
-d, --install-dir DIR Installation directory (default: /usr/local/bin)
|
|
-v, --version VERSION Specific version to install (default: latest)
|
|
-p, --platform PLATFORM Force specific platform (auto-detected by default)
|
|
-f, --force Force installation even if already installed
|
|
--dry-run Show what would be done without doing it
|
|
--quiet Suppress non-essential output
|
|
-h, --help Show this help message
|
|
|
|
EXAMPLES:
|
|
# Install latest version to default location
|
|
$0
|
|
|
|
# Install to custom directory
|
|
$0 --install-dir ~/.local/bin
|
|
|
|
# Install specific version
|
|
$0 --version v1.2.3
|
|
|
|
# Force specific platform
|
|
$0 --platform linux-amd64
|
|
|
|
PLATFORMS:
|
|
linux-amd64 Linux x86_64
|
|
linux-arm64 Linux ARM64
|
|
darwin-amd64 macOS Intel
|
|
darwin-arm64 macOS Apple Silicon
|
|
windows-amd64 Windows x86_64
|
|
|
|
REQUIREMENTS:
|
|
- curl or wget
|
|
- tar (for Unix-like systems)
|
|
- nushell (nu command should be available)
|
|
- Write permissions to installation directory
|
|
|
|
MORE INFO:
|
|
Repository: ${BASE_URL}
|
|
Releases: ${BASE_URL}/releases
|
|
EOF
|
|
}
|
|
|
|
# Platform detection
|
|
detect_platform() {
|
|
local os=""
|
|
local arch=""
|
|
|
|
# Detect OS
|
|
case "$(uname -s)" in
|
|
Linux*) os="linux" ;;
|
|
Darwin*) os="darwin" ;;
|
|
CYGWIN*|MINGW*|MSYS*) os="windows" ;;
|
|
*)
|
|
error "Unsupported operating system: $(uname -s)"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Detect architecture
|
|
case "$(uname -m)" in
|
|
x86_64|amd64) arch="amd64" ;;
|
|
aarch64|arm64) arch="arm64" ;;
|
|
armv7l) arch="arm32" ;;
|
|
*)
|
|
error "Unsupported architecture: $(uname -m)"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
echo "${os}-${arch}"
|
|
}
|
|
|
|
# Check if command exists
|
|
command_exists() {
|
|
command -v "$1" >/dev/null 2>&1
|
|
}
|
|
|
|
# Check prerequisites
|
|
check_prerequisites() {
|
|
log "Checking prerequisites..."
|
|
|
|
# Check for download tools
|
|
if ! command_exists curl && ! command_exists wget; then
|
|
error "Neither curl nor wget found. Please install one of them."
|
|
exit 1
|
|
fi
|
|
|
|
# Check for tar (Unix-like systems)
|
|
if [ "$PLATFORM" != "windows-amd64" ] && ! command_exists tar; then
|
|
error "tar command not found. Please install tar."
|
|
exit 1
|
|
fi
|
|
|
|
# Check for nushell
|
|
if ! command_exists nu; then
|
|
warn "nushell (nu) not found in PATH. You'll need to install nushell to use these plugins."
|
|
warn "Visit: https://www.nushell.sh/book/installation.html"
|
|
fi
|
|
|
|
# Check write permissions
|
|
if [ ! -w "$(dirname "$INSTALL_DIR")" ]; then
|
|
error "No write permission to $(dirname "$INSTALL_DIR")"
|
|
error "Try running with sudo or choose a different directory with --install-dir"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Download file
|
|
download_file() {
|
|
local url="$1"
|
|
local output="$2"
|
|
|
|
log "Downloading: $url"
|
|
|
|
if command_exists curl; then
|
|
curl -L --fail --progress-bar "$url" -o "$output"
|
|
elif command_exists wget; then
|
|
wget --progress=bar:force:noscroll "$url" -O "$output"
|
|
else
|
|
error "No download tool available"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Get latest release info
|
|
get_latest_release() {
|
|
local api_url="${API_URL}/releases/latest"
|
|
|
|
log "Fetching latest release information..."
|
|
|
|
if command_exists curl; then
|
|
curl -s "$api_url"
|
|
elif command_exists wget; then
|
|
wget -qO- "$api_url"
|
|
else
|
|
error "No download tool available"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Get specific release info
|
|
get_release_info() {
|
|
local version="$1"
|
|
local api_url="${API_URL}/releases/tags/${version}"
|
|
|
|
log "Fetching release information for ${version}..."
|
|
|
|
if command_exists curl; then
|
|
curl -s "$api_url"
|
|
elif command_exists wget; then
|
|
wget -qO- "$api_url"
|
|
else
|
|
error "No download tool available"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Parse JSON (simple parsing for our needs)
|
|
parse_json_value() {
|
|
local json="$1"
|
|
local key="$2"
|
|
|
|
echo "$json" | sed -n "s/.*\"$key\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" | head -1
|
|
}
|
|
|
|
# Find download URL for platform
|
|
find_download_url() {
|
|
local release_json="$1"
|
|
local platform="$2"
|
|
|
|
# Look for asset with platform name
|
|
echo "$release_json" | grep -o '"browser_download_url":"[^"]*"' | \
|
|
grep "$platform" | \
|
|
head -1 | \
|
|
sed 's/"browser_download_url":"\([^"]*\)"/\1/'
|
|
}
|
|
|
|
# Extract archive
|
|
extract_archive() {
|
|
local archive="$1"
|
|
local dest_dir="$2"
|
|
|
|
log "Extracting archive to $dest_dir..."
|
|
|
|
case "$archive" in
|
|
*.tar.gz)
|
|
tar -xzf "$archive" -C "$dest_dir"
|
|
;;
|
|
*.zip)
|
|
if command_exists unzip; then
|
|
unzip -q "$archive" -d "$dest_dir"
|
|
else
|
|
error "unzip command not found. Please install unzip to extract .zip files."
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
error "Unsupported archive format: $archive"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Install plugins
|
|
install_plugins() {
|
|
local extract_dir="$1"
|
|
|
|
log "Installing plugins to $INSTALL_DIR..."
|
|
|
|
# Find the extracted directory (might be nested)
|
|
local plugin_dir=""
|
|
if [ -d "$extract_dir" ]; then
|
|
# Look for nu_plugin_* files directly or in subdirectories
|
|
plugin_dir=$(find "$extract_dir" -name "nu_plugin_*" -type f -executable | head -1 | xargs dirname)
|
|
|
|
if [ -z "$plugin_dir" ]; then
|
|
# Try to find a directory with plugins
|
|
plugin_dir=$(find "$extract_dir" -type d -name "*nushell-plugins*" | head -1)
|
|
if [ -z "$plugin_dir" ]; then
|
|
plugin_dir="$extract_dir"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ ! -d "$plugin_dir" ]; then
|
|
error "Could not find plugin directory in extracted archive"
|
|
exit 1
|
|
fi
|
|
|
|
log "Found plugins in: $plugin_dir"
|
|
|
|
# Create install directory if it doesn't exist
|
|
mkdir -p "$INSTALL_DIR"
|
|
|
|
# Copy plugin binaries
|
|
local installed_count=0
|
|
for plugin in "$plugin_dir"/nu_plugin_*; do
|
|
if [ -f "$plugin" ] && [ -x "$plugin" ]; then
|
|
local plugin_name=$(basename "$plugin")
|
|
local dest_path="$INSTALL_DIR/$plugin_name"
|
|
|
|
if [ "$DRY_RUN" = "true" ]; then
|
|
log "Would install: $plugin_name -> $dest_path"
|
|
else
|
|
log "Installing: $plugin_name"
|
|
cp "$plugin" "$dest_path"
|
|
chmod +x "$dest_path"
|
|
fi
|
|
|
|
installed_count=$((installed_count + 1))
|
|
fi
|
|
done
|
|
|
|
if [ $installed_count -eq 0 ]; then
|
|
error "No plugin binaries found to install"
|
|
exit 1
|
|
fi
|
|
|
|
success "Installed $installed_count plugins"
|
|
|
|
# Copy installation script if available
|
|
if [ -f "$plugin_dir/install_nu_plugins.nu" ] && [ "$DRY_RUN" != "true" ]; then
|
|
log "Running nushell installation script..."
|
|
if command_exists nu; then
|
|
cd "$plugin_dir"
|
|
nu install_nu_plugins.nu --bin-path "$INSTALL_DIR" || {
|
|
warn "Installation script failed, but binaries were copied"
|
|
}
|
|
else
|
|
warn "Nushell not available, skipping automatic plugin registration"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Cleanup temporary files
|
|
cleanup() {
|
|
if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then
|
|
log "Cleaning up temporary files..."
|
|
rm -rf "$TEMP_DIR"
|
|
fi
|
|
}
|
|
|
|
# Main installation function
|
|
main() {
|
|
# Set up cleanup trap
|
|
trap cleanup EXIT
|
|
|
|
log "Nushell Plugins Universal Installer"
|
|
log "Repository: $GITHUB_REPO"
|
|
|
|
# Auto-detect platform if not specified
|
|
if [ -z "$PLATFORM" ]; then
|
|
PLATFORM=$(detect_platform)
|
|
log "Detected platform: $PLATFORM"
|
|
else
|
|
log "Using specified platform: $PLATFORM"
|
|
fi
|
|
|
|
# Check prerequisites
|
|
check_prerequisites
|
|
|
|
# Get release information
|
|
local release_json=""
|
|
if [ -z "$VERSION" ]; then
|
|
log "Getting latest release..."
|
|
release_json=$(get_latest_release)
|
|
VERSION=$(parse_json_value "$release_json" "tag_name")
|
|
else
|
|
log "Getting release $VERSION..."
|
|
release_json=$(get_release_info "$VERSION")
|
|
fi
|
|
|
|
if [ -z "$release_json" ] || [ "$release_json" = "null" ]; then
|
|
error "Failed to get release information"
|
|
exit 1
|
|
fi
|
|
|
|
log "Version: $VERSION"
|
|
|
|
# Find download URL
|
|
local download_url=$(find_download_url "$release_json" "$PLATFORM")
|
|
|
|
if [ -z "$download_url" ]; then
|
|
error "No download found for platform: $PLATFORM"
|
|
error "Available platforms can be found at: ${BASE_URL}/releases/tag/${VERSION}"
|
|
exit 1
|
|
fi
|
|
|
|
log "Download URL: $download_url"
|
|
|
|
if [ "$DRY_RUN" = "true" ]; then
|
|
log "DRY RUN: Would download and install from $download_url"
|
|
log "DRY RUN: Would install to $INSTALL_DIR"
|
|
return 0
|
|
fi
|
|
|
|
# Create temporary directory
|
|
TEMP_DIR=$(mktemp -d)
|
|
local archive_name=$(basename "$download_url")
|
|
local archive_path="$TEMP_DIR/$archive_name"
|
|
|
|
# Download archive
|
|
download_file "$download_url" "$archive_path"
|
|
|
|
# Verify download
|
|
if [ ! -f "$archive_path" ]; then
|
|
error "Download failed: $archive_path not found"
|
|
exit 1
|
|
fi
|
|
|
|
# Extract archive
|
|
extract_archive "$archive_path" "$TEMP_DIR"
|
|
|
|
# Install plugins
|
|
install_plugins "$TEMP_DIR"
|
|
|
|
success "Installation completed successfully!"
|
|
|
|
# Show next steps
|
|
echo ""
|
|
echo "🎉 Nushell plugins have been installed to: $INSTALL_DIR"
|
|
echo ""
|
|
echo "📝 Next steps:"
|
|
echo " 1. Make sure $INSTALL_DIR is in your PATH"
|
|
echo " 2. In nushell, register plugins with:"
|
|
|
|
# List installed plugins
|
|
for plugin in "$INSTALL_DIR"/nu_plugin_*; do
|
|
if [ -f "$plugin" ] && [ -x "$plugin" ]; then
|
|
echo " plugin add $plugin"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "💡 Or run this one-liner in nushell:"
|
|
echo " ls $INSTALL_DIR/nu_plugin_* | each { |plugin| plugin add \$plugin.name }"
|
|
echo ""
|
|
echo "🔗 More info: ${BASE_URL}"
|
|
}
|
|
|
|
# Parse command line arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-d|--install-dir)
|
|
INSTALL_DIR="$2"
|
|
shift 2
|
|
;;
|
|
-v|--version)
|
|
VERSION="$2"
|
|
shift 2
|
|
;;
|
|
-p|--platform)
|
|
PLATFORM="$2"
|
|
shift 2
|
|
;;
|
|
-f|--force)
|
|
FORCE=true
|
|
shift
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
--quiet)
|
|
QUIET=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
error "Unknown option: $1"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Run main function
|
|
main |