diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b3cfbd6..e90a83b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: matrix: include: - os: ubuntu-latest - target: x86_64-unknown-linux-gnu + target: x86_64-unknown-linux-musl artifact_name: uvup asset_name: uvup-linux-x86_64 - os: macos-latest @@ -36,6 +36,10 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Install musl tools (Linux) + if: matrix.target == 'x86_64-unknown-linux-musl' + run: sudo apt-get update && sudo apt-get install -y musl-tools + - name: Install Rust uses: dtolnay/rust-toolchain@stable with: diff --git a/README.md b/README.md index 419580b..40350cf 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,86 @@ uvup aims to be a companion tool for uv, providing a familiar conda-like interfa ## Installation -### For Users +### Quick Install (Recommended) -Coming soon - prebuilt binaries will be available on GitHub Releases. +**Linux and macOS:** +```bash +curl -fsSL https://raw.githubusercontent.com/KercyDing/uvup/main/scripts/install.sh | sh +``` + +**Windows (PowerShell):** +```powershell +Invoke-RestMethod https://raw.githubusercontent.com/KercyDing/uvup/main/scripts/install.ps1 | Invoke-Expression +``` + +The installation script will automatically configure shell integration for you. Restart your terminal or run: + +```bash +# Linux/macOS +source ~/.zshrc # or ~/.bashrc for bash + +# Windows (PowerShell) +. $PROFILE +``` + +### Manual Installation + +Download the latest release for your platform from [GitHub Releases](https://github.com/KercyDing/uvup/releases): + +**Linux:** +```bash +# Download and install +wget https://github.com/KercyDing/uvup/releases/latest/download/uvup-linux-x86_64 +chmod +x uvup-linux-x86_64 +sudo mv uvup-linux-x86_64 /usr/local/bin/uvup + +# Initialize shell integration +echo 'eval "$(uvup init)"' >> ~/.bashrc # or ~/.zshrc for zsh +source ~/.bashrc +``` + +**macOS:** +```bash +# Download and install (Apple Silicon) +wget https://github.com/KercyDing/uvup/releases/latest/download/uvup-macos-arm64 +chmod +x uvup-macos-arm64 +sudo mv uvup-macos-arm64 /usr/local/bin/uvup + +# OR for Intel Macs +wget https://github.com/KercyDing/uvup/releases/latest/download/uvup-macos-x86_64 +chmod +x uvup-macos-x86_64 +sudo mv uvup-macos-x86_64 /usr/local/bin/uvup + +# Initialize shell integration +echo 'eval "$(uvup init)"' >> ~/.zshrc +source ~/.zshrc +``` + +**Windows:** + +1. Download [uvup-windows-x86_64.exe](https://github.com/KercyDing/uvup/releases/latest/download/uvup-windows-x86_64.exe) + +2. Create directory and move the binary: + ```powershell + New-Item -ItemType Directory -Force -Path "$env:LOCALAPPDATA\Programs\uvup" + Move-Item uvup-windows-x86_64.exe "$env:LOCALAPPDATA\Programs\uvup\uvup.exe" + ``` + +3. Add to PATH: + - Press `Win + R`, type `sysdm.cpl`, press Enter + - Go to "Advanced" tab → "Environment Variables" + - Under "User variables", select "Path" and click "Edit" + - Click "New" and add: `%LOCALAPPDATA%\Programs\uvup` + - Click OK to save + +4. Initialize shell integration (restart terminal first): + ```powershell + # Add to profile for all PowerShell hosts + Add-Content -Path $PROFILE.CurrentUserAllHosts -Value "`nInvoke-Expression ((uvup init) -join `"``n`")" + + # Load in current session + Invoke-Expression ((uvup init) -join "`n") + ``` ### For Developers @@ -33,12 +110,23 @@ cargo install --path . ``` 3. Initialize shell integration: + +**Linux/macOS:** ```bash # Add to your shell configuration echo 'eval "$(uvup init)"' >> ~/.zshrc # or ~/.bashrc for bash source ~/.zshrc ``` +**Windows (PowerShell):** +```powershell +# Add to profile for all PowerShell hosts +Add-Content -Path $PROFILE.CurrentUserAllHosts -Value "`nInvoke-Expression ((uvup init) -join `"``n`")" + +# Load in current session +Invoke-Expression ((uvup init) -join "`n") +``` + #### Development Workflow During development, you have several options: @@ -64,20 +152,20 @@ cargo build ## Planned Features -### MVP (v0.1.0) - ✅ Completed +### MVP (v0.1.0) - Completed -- ✅ `uvup init` - Shell integration -- ✅ `uvup create ` - Create environments -- ✅ `uvup activate ` - Activate environments (via shell hook) -- ✅ `uvup list` - List all environments -- ✅ `uvup remove ` - Remove environments +- [x] `uvup init` - Shell integration (Bash, Zsh, Fish, PowerShell) +- [x] `uvup create ` - Create environments +- [x] `uvup activate ` - Activate environments (via shell hook) +- [x] `uvup list` - List all environments +- [x] `uvup remove ` - Remove environments ### Future Versions - `uvup default ` - Set default environment (auto-activate on new terminal) - `uvup undefault` - Remove default environment -- Enhanced shell support (Fish, PowerShell) -- Cross-platform distribution (Homebrew, Scoop, Winget) +- Installation via package managers (Homebrew, Scoop, Winget) +- Enhanced `list` command with more environment details ## Usage diff --git a/scripts/install.ps1 b/scripts/install.ps1 new file mode 100644 index 0000000..3d02db2 --- /dev/null +++ b/scripts/install.ps1 @@ -0,0 +1,82 @@ +# Check execution policy +$executionPolicy = Get-ExecutionPolicy -Scope CurrentUser +if ($executionPolicy -eq "Restricted" -or $executionPolicy -eq "Undefined") { + Write-Host "Error: PowerShell script execution is disabled on this system." -ForegroundColor Red + Write-Host "" + Write-Host "To allow script execution, run the following command:" -ForegroundColor Yellow + Write-Host " Set-ExecutionPolicy RemoteSigned -Scope CurrentUser" -ForegroundColor White + Write-Host "" + Write-Host "Then run this installation script again." -ForegroundColor Yellow + exit 1 +} + +$ErrorActionPreference = "Stop" + +Write-Host "Downloading uvup for Windows..." -ForegroundColor Green + +$DOWNLOAD_URL = "https://github.com/KercyDing/uvup/releases/latest/download/uvup-windows-x86_64.exe" +$INSTALL_DIR = "$env:LOCALAPPDATA\Programs\uvup" +$INSTALL_PATH = "$INSTALL_DIR\uvup.exe" + +# Create install directory +New-Item -ItemType Directory -Force -Path $INSTALL_DIR | Out-Null + +# Download binary +try { + Invoke-WebRequest -Uri $DOWNLOAD_URL -OutFile $INSTALL_PATH -UseBasicParsing +} catch { + Write-Host "Error: Failed to download uvup" -ForegroundColor Red + exit 1 +} + +Write-Host "uvup installed to $INSTALL_PATH" -ForegroundColor Green + +# Add to PATH if not already present +$userPath = [Environment]::GetEnvironmentVariable("Path", "User") +if ($userPath -notlike "*$INSTALL_DIR*") { + Write-Host "Adding $INSTALL_DIR to PATH..." -ForegroundColor Yellow + [Environment]::SetEnvironmentVariable("Path", "$userPath;$INSTALL_DIR", "User") + $env:Path = "$env:Path;$INSTALL_DIR" + Write-Host "PATH updated. You may need to restart your terminal." -ForegroundColor Yellow +} + +Write-Host "" +Write-Host "uvup installed successfully!" -ForegroundColor Green + +# Add uvup initialization to PowerShell profile +# Use CurrentUserAllHosts (profile.ps1) - works for all PowerShell hosts +$PROFILE_PATH = $PROFILE.CurrentUserAllHosts +if (-not $PROFILE_PATH) { + $PROFILE_PATH = "$env:USERPROFILE\Documents\PowerShell\profile.ps1" +} + +Write-Host "" +Write-Host "Configuring PowerShell profile..." -ForegroundColor Cyan + +# Create profile if it doesn't exist +if (-not (Test-Path $PROFILE_PATH)) { + $profileDir = Split-Path $PROFILE_PATH -Parent + if (-not (Test-Path $profileDir)) { + New-Item -ItemType Directory -Path $profileDir -Force | Out-Null + } + New-Item -ItemType File -Path $PROFILE_PATH -Force | Out-Null + Write-Host "Created profile at: $PROFILE_PATH" -ForegroundColor Green +} + +# Check if already exists +$profileContent = Get-Content $PROFILE_PATH -Raw -ErrorAction SilentlyContinue +if ($profileContent -match 'uvup init.*Invoke-Expression') { + Write-Host "uvup initialization already exists in profile" -ForegroundColor Yellow +} else { + # Add initialization line + $initLine = 'Invoke-Expression ((uvup init) -join "`n")' + Add-Content -Path $PROFILE_PATH -Value "`n# uvup initialization" + Add-Content -Path $PROFILE_PATH -Value $initLine + Write-Host "Added uvup initialization to profile" -ForegroundColor Green +} + +Write-Host "" +Write-Host "To start using uvup, run:" -ForegroundColor Cyan +Write-Host " . `$PROFILE" -ForegroundColor White +Write-Host "" +Write-Host "Or restart your terminal." -ForegroundColor Cyan diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100644 index 0000000..2d1f30d --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,132 @@ +#!/bin/sh +set -e + +# uvup installer script + +# Detect OS and architecture +OS="$(uname -s)" +ARCH="$(uname -m)" + +# Determine download URL +case "$OS" in + Linux*) + if [ "$ARCH" = "x86_64" ]; then + BINARY="uvup-linux-x86_64" + else + echo "Error: Unsupported architecture $ARCH for Linux" + exit 1 + fi + ;; + Darwin*) + if [ "$ARCH" = "arm64" ]; then + BINARY="uvup-macos-arm64" + elif [ "$ARCH" = "x86_64" ]; then + BINARY="uvup-macos-x86_64" + else + echo "Error: Unsupported architecture $ARCH for macOS" + exit 1 + fi + ;; + *) + echo "Error: Unsupported operating system $OS" + exit 1 + ;; +esac + +DOWNLOAD_URL="https://github.com/KercyDing/uvup/releases/latest/download/$BINARY" + +echo "Downloading uvup for $OS $ARCH..." +TEMP_FILE=$(mktemp) +if command -v curl >/dev/null 2>&1; then + curl -fsSL "$DOWNLOAD_URL" -o "$TEMP_FILE" +elif command -v wget >/dev/null 2>&1; then + wget -q "$DOWNLOAD_URL" -O "$TEMP_FILE" +else + echo "Error: curl or wget is required" + exit 1 +fi + +# Make executable +chmod +x "$TEMP_FILE" + +# Determine install directory +if [ -w "/usr/local/bin" ]; then + INSTALL_DIR="/usr/local/bin" + sudo="" +elif [ "$(id -u)" -eq 0 ]; then + INSTALL_DIR="/usr/local/bin" + sudo="" +else + INSTALL_DIR="$HOME/.local/bin" + sudo="" + mkdir -p "$INSTALL_DIR" +fi + +# Move to install directory +echo "Installing uvup to $INSTALL_DIR..." +if [ -n "$sudo" ]; then + $sudo mv "$TEMP_FILE" "$INSTALL_DIR/uvup" +else + mv "$TEMP_FILE" "$INSTALL_DIR/uvup" +fi + +echo "uvup installed successfully!" + +# Check if install directory is in PATH +case ":$PATH:" in + *":$INSTALL_DIR:"*) ;; + *) + echo "" + echo "Warning: $INSTALL_DIR is not in your PATH" + echo "Add the following to your shell configuration file:" + echo " export PATH=\"$INSTALL_DIR:\$PATH\"" + ;; +esac + +# Detect shell and provide init instructions +SHELL_NAME=$(basename "$SHELL") +SHELL_RC="" +INIT_LINE="" + +case "$SHELL_NAME" in + bash) + SHELL_RC="$HOME/.bashrc" + INIT_LINE='eval "$(uvup init)"' + ;; + zsh) + SHELL_RC="$HOME/.zshrc" + INIT_LINE='eval "$(uvup init)"' + ;; + fish) + SHELL_RC="$HOME/.config/fish/config.fish" + INIT_LINE='uvup init | source' + ;; + *) + SHELL_RC="$HOME/.profile" + INIT_LINE='eval "$(uvup init)"' + ;; +esac + +echo "" +echo "Configuring shell integration..." + +# Check if already exists +if [ -f "$SHELL_RC" ] && grep -q "eval.*uvup init\|uvup init.*source" "$SHELL_RC"; then + echo "uvup initialization already exists in $SHELL_RC" +else + # Create shell RC if it doesn't exist + touch "$SHELL_RC" + + # Add init line + echo "" >> "$SHELL_RC" + echo "# uvup initialization" >> "$SHELL_RC" + echo "$INIT_LINE" >> "$SHELL_RC" + + echo "Added uvup initialization to $SHELL_RC" +fi + +echo "" +echo "To start using uvup, run:" +echo " source $SHELL_RC" +echo "" +echo "Or restart your terminal." diff --git a/scripts/uninstall.ps1 b/scripts/uninstall.ps1 new file mode 100644 index 0000000..3b7c7d3 --- /dev/null +++ b/scripts/uninstall.ps1 @@ -0,0 +1,97 @@ +$ErrorActionPreference = "Stop" + +Write-Host "Uninstalling uvup..." -ForegroundColor Green + +# Find and remove uvup binaries from common locations +$LOCATIONS = @( + "$env:LOCALAPPDATA\Programs\uvup\uvup.exe", + "$env:USERPROFILE\.cargo\bin\uvup.exe" +) + +$FOUND = $false + +foreach ($path in $LOCATIONS) { + if (Test-Path $path) { + Write-Host "Removing $path..." -ForegroundColor Yellow + Remove-Item -Path $path -Force + Write-Host " Removed: $path" -ForegroundColor Green + $FOUND = $true + + # Remove directory if empty (only for Programs\uvup) + $dir = Split-Path $path -Parent + if ($dir -like "*Programs\uvup" -and (Test-Path $dir)) { + if ((Get-ChildItem -Path $dir -Force | Measure-Object).Count -eq 0) { + Remove-Item -Path $dir -Force + Write-Host " Removed directory: $dir" -ForegroundColor Green + } + } + } +} + +if (-not $FOUND) { + Write-Host "Warning: uvup binary not found in common locations" -ForegroundColor Yellow +} + +# Remove data directory +$UVUP_DIR = "$env:USERPROFILE\.uvup" +if (Test-Path $UVUP_DIR) { + Write-Host "" + Write-Host "Found uvup data directory: $UVUP_DIR" -ForegroundColor Cyan + $response = Read-Host "Do you want to remove all virtual environments? [y/N]" + if ($response -match '^[yY]') { + Remove-Item -Path $UVUP_DIR -Recurse -Force + Write-Host " Removed: $UVUP_DIR" -ForegroundColor Green + } else { + Write-Host " Kept: $UVUP_DIR" -ForegroundColor Yellow + } +} + +# Remove from PATH +$INSTALL_DIR = "$env:LOCALAPPDATA\Programs\uvup" +$userPath = [Environment]::GetEnvironmentVariable("Path", "User") +if ($userPath -like "*$INSTALL_DIR*") { + Write-Host "" + Write-Host "Removing $INSTALL_DIR from PATH..." -ForegroundColor Yellow + + $pathArray = $userPath -split ';' | Where-Object { $_ -ne $INSTALL_DIR -and $_ -ne "" } + $newPath = $pathArray -join ';' + + [Environment]::SetEnvironmentVariable("Path", $newPath, "User") + $env:Path = $env:Path -replace [regex]::Escape(";$INSTALL_DIR"), "" + $env:Path = $env:Path -replace [regex]::Escape("$INSTALL_DIR;"), "" + + Write-Host " Removed from PATH" -ForegroundColor Green +} + +# Remove from PowerShell profile +# Only check profile.ps1 (CurrentUserAllHosts) since install.ps1 only writes there +$PROFILE_PATH = $PROFILE.CurrentUserAllHosts +if (-not $PROFILE_PATH) { + $PROFILE_PATH = "$env:USERPROFILE\Documents\PowerShell\profile.ps1" +} + +if (Test-Path $PROFILE_PATH) { + $profileContent = Get-Content $PROFILE_PATH -Raw -ErrorAction SilentlyContinue + if ($profileContent -and $profileContent -match 'uvup init') { + Write-Host "" + Write-Host "Removing uvup from PowerShell profile..." -ForegroundColor Yellow + + # Read as lines and filter out uvup-related lines + $lines = Get-Content $PROFILE_PATH + $filteredLines = $lines | Where-Object { + $_ -notmatch 'uvup init' -and + $_ -notmatch '^\s*#\s*uvup initialization\s*$' + } + + # Write back + $filteredLines | Out-File -FilePath $PROFILE_PATH -Encoding utf8 -Force + + Write-Host " Removed uvup initialization from profile" -ForegroundColor Green + } +} + +Write-Host "" +Write-Host "To start using uvup, run:" -ForegroundColor Cyan +Write-Host " . `$PROFILE" -ForegroundColor White +Write-Host "" +Write-Host "Or restart your terminal." -ForegroundColor Cyan diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh new file mode 100644 index 0000000..665f6a8 --- /dev/null +++ b/scripts/uninstall.sh @@ -0,0 +1,120 @@ +#!/bin/sh +set -e + +# uvup uninstaller script + +echo "Uninstalling uvup..." + +# Find uvup binary locations +FOUND=0 +BINARY_PATHS="" + +# Check common installation locations +for dir in "/usr/local/bin" "$HOME/.local/bin" "$HOME/.cargo/bin"; do + if [ -f "$dir/uvup" ]; then + BINARY_PATHS="$BINARY_PATHS $dir/uvup" + FOUND=1 + fi +done + +# Remove binary files +if [ $FOUND -eq 1 ]; then + for path in $BINARY_PATHS; do + echo "Removing $path..." + if [ -w "$(dirname "$path")" ]; then + rm -f "$path" + echo " Removed: $path" + else + sudo rm -f "$path" + echo " Removed: $path (required sudo)" + fi + done +else + echo "Warning: uvup binary not found in common locations" +fi + +# Remove data directory +UVUP_DIR="$HOME/.uvup" +if [ -d "$UVUP_DIR" ]; then + echo "" + echo "Found uvup data directory: $UVUP_DIR" + printf "Do you want to remove all virtual environments? [y/N] " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + rm -rf "$UVUP_DIR" + echo " Removed: $UVUP_DIR" + ;; + *) + echo " Kept: $UVUP_DIR" + ;; + esac +fi + +# Detect shell and provide cleanup instructions +SHELL_NAME=$(basename "$SHELL") +SHELL_RC="" + +case "$SHELL_NAME" in + bash) + SHELL_RC="$HOME/.bashrc" + ;; + zsh) + SHELL_RC="$HOME/.zshrc" + ;; + fish) + SHELL_RC="$HOME/.config/fish/config.fish" + ;; + *) + SHELL_RC="$HOME/.profile" + ;; +esac + +echo "" +echo "Please manually remove uvup initialization from your shell config:" +echo " Edit: $SHELL_RC" +echo " Remove lines containing: eval \"\$(uvup init)\"" +echo "" + +# Check for uvup in shell config +if [ -f "$SHELL_RC" ] && grep -q "eval.*uvup init\|uvup init.*source" "$SHELL_RC"; then + echo "Found uvup initialization in $SHELL_RC" + printf "Do you want to automatically remove it? [y/N] " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + cp "$SHELL_RC" "$SHELL_RC.uvup-backup" + # Remove uvup lines + if [ "$SHELL_NAME" = "fish" ]; then + # Fish uses different syntax + sed -i.tmp '/uvup init.*source/d' "$SHELL_RC" + sed -i.tmp '/# uvup initialization/d' "$SHELL_RC" + else + # Bash/Zsh + sed -i.tmp '/eval.*uvup init/d' "$SHELL_RC" + sed -i.tmp '/# uvup initialization/d' "$SHELL_RC" + fi + rm -f "$SHELL_RC.tmp" + echo " Removed uvup initialization from $SHELL_RC" + echo " Backup saved to: $SHELL_RC.uvup-backup" + ;; + *) + echo " Skipped automatic removal" + ;; + esac +fi + +# Check PATH modifications +echo "" +echo "Checking PATH for uvup directories..." +case ":$PATH:" in + *":.local/bin:"*) + echo " Found ~/.local/bin in PATH (may be used by other tools)" + ;; +esac + +echo "" +echo "uvup uninstalled successfully!" +echo "" +echo "Note: Please restart your shell or run:" +echo " source $SHELL_RC"