476 lines
12 KiB
Bash
476 lines
12 KiB
Bash
![]() |
#!/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
|