diff --git a/setup.ps1 b/setup.ps1 new file mode 100644 index 000000000..551331f3f --- /dev/null +++ b/setup.ps1 @@ -0,0 +1,214 @@ +# Set the console color to Matrix theme and clear the console +$host.UI.RawUI.BackgroundColor = "Black" +$host.UI.RawUI.ForegroundColor = "Green" +Clear-Host + +# Set the log file path and initialize variables +$LogFilePath = Join-Path $env:TEMP "script_log.txt" +$ProjectDir = "G:\Downloads\GitHub\freqtrade" +$RequirementFiles = @("requirements.txt", "requirements-dev.txt", "requirements-hyperopt.txt", "requirements-freqai.txt", "requirements-plot.txt") +$VenvName = ".venv" + +function Write-Log { + param ( + [string]$Message, + [string]$Level = 'INFO' + ) + switch ($Level) { + 'INFO' { Write-Host $Message -ForegroundColor Green } + 'WARNING' { Write-Host $Message -ForegroundColor Yellow } + 'ERROR' { Write-Host $Message -ForegroundColor Red } + 'PROMPT' { Write-Host $Message -ForegroundColor Cyan } + } + "${Level}: $Message" | Out-File $LogFilePath -Append +} + +function Get-UserSelection { + param ( + [string]$prompt, + [string[]]$options, + [string]$defaultChoice = 'A' + ) + + Write-Log "$prompt`n" -Level 'PROMPT' + for ($i = 0; $i -lt $options.Length; $i++) { + Write-Log "$([char](65 + $i)). $($options[$i])" -Level 'PROMPT' + } + + Write-Log "`nSelect one or more options by typing the corresponding letters, separated by commas." -Level 'PROMPT' + $inputPath = Read-Host + if ([string]::IsNullOrEmpty($inputPath)) { + $inputPath = $defaultChoice + } + + # Ensure $inputPath is treated as a string and split it by commas + $inputPath = [string]$inputPath + $selections = $inputPath.Split(',') | ForEach-Object { + $_.Trim().ToUpper() + } + + # Convert each selection from letter to index + $indices = $selections | ForEach-Object { + if ($_ -match '^[A-Z]$') { + # Ensure the input is a single uppercase letter + [int][char]$_ - [int][char]'A' + } + else { + Write-Log "Invalid input: $_. Please enter letters between A and Z." -Level 'ERROR' + continue + } + } + + return $indices +} + +function Exit-Script { + param ( + [int]$exitCode, + [bool]$isSubShell = $true + ) + + if ($OldVirtualPath) { + $env:PATH = $OldVirtualPath + } + + # Check if the script is exiting with an error and it's not a subshell + if ($exitCode -ne 0 -and $isSubShell) { + Write-Log "Script failed. Would you like to open the log file? (Y/N)" -Level 'PROMPT' + $openLog = Read-Host + if ($openLog -eq 'Y' -or $openLog -eq 'y') { + Start-Process notepad.exe -ArgumentList $LogFilePath + } + } + + Write-Log "Press any key to exit..." + $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null + exit $exitCode +} + +# Function to handle installation and conflict resolution +function Install-And-Resolve { + param ([string]$InputPath) + if (-not $InputPath) { + Write-Log "ERROR: No input provided for installation." -Level 'ERROR' + Exit-Script -exitCode 1 + } + Write-Log "Installing $InputPath..." + $installCmd = if (Test-Path $InputPath) { $VenvPip + @('install', '-r', $InputPath) } else { $VenvPip + @('install', $InputPath) } + $output = & $installCmd[0] $installCmd[1..$installCmd.Length] 2>&1 + $output | Out-File $LogFilePath -Append + if ($LASTEXITCODE -ne 0) { + Write-Log "Conflict detected, attempting to resolve..." -Level 'ERROR' + & $VenvPip[0] $VenvPip[1..$VenvPip.Length] 'check' | Out-File "conflicts.txt" + Exit-Script -exitCode 1 + } +} + +# Function to get the installed Python version tag for wheel compatibility +function Get-PythonVersionTag { + $pythonVersion = & python -c "import sys; print(f'cp{sys.version_info.major}{sys.version_info.minor}')" + $architecture = & python -c "import platform; print('win_amd64' if platform.machine().endswith('64') else 'win32')" + return "$pythonVersion-$architecture" +} + +# Check for admin privileges and elevate if necessary +if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + Write-Log "Requesting administrative privileges..." -Level 'ERROR' + Start-Process PowerShell -ArgumentList "-File `"$PSCommandPath`"" -Verb RunAs + Exit-Script -exitCode 1 -isSubShell $false +} + +# Log file setup +"Admin rights confirmed" | Out-File $LogFilePath -Append +"Starting the script operations..." | Out-File $LogFilePath -Append + +# Navigate to the project directory +Set-Location -Path $ProjectDir +"Current directory: $(Get-Location)" | Out-File $LogFilePath -Append + +# Define the path to the Python executable in the virtual environment +$VenvPython = Join-Path $ProjectDir "$VenvName\Scripts\python.exe" + +# Check if the virtual environment exists, if not, create it +if (-Not (Test-Path $VenvPython)) { + Write-Log "Virtual environment not found. Creating virtual environment..." -IsError $false + python -m venv "$VenvName" + if (-Not (Test-Path $VenvPython)) { + Write-Log "Failed to create virtual environment." -Level 'ERROR' + Exit-Script -exitCode 1 + } + Write-Log "Virtual environment created successfully." -IsError $false +} + +# Define the pip command using the Python executable +$VenvPip = @($VenvPython, '-m', 'pip') + +# Activate the virtual environment +$OldVirtualPath = $env:PATH +$env:PATH = "$ProjectDir\$VenvName\Scripts;$env:PATH" + +# Ensure setuptools is installed using the virtual environment's pip +Write-Log "Ensuring setuptools is installed..." +& $VenvPip[0] $VenvPip[1..$VenvPip.Length] 'install', '-v', 'setuptools' | Out-File $LogFilePath -Append 2>&1 +if ($LASTEXITCODE -ne 0) { + Write-Log "Failed to install setuptools." -Level 'ERROR' + Exit-Script -exitCode 1 +} + +# Pull latest updates +Write-Log "Pulling latest updates..." +& git pull | Out-File $LogFilePath -Append 2>&1 +if ($LASTEXITCODE -ne 0) { + Write-Log "Failed to pull updates from Git." -Level 'ERROR' + Exit-Script -exitCode 1 +} + +# Install TA-Lib using the virtual environment's pip +Write-Log "Installing TA-Lib using virtual environment's pip..." +& $VenvPip[0] $VenvPip[1..$VenvPip.Length] 'install', '--find-links=build_helpers\', '--prefer-binary', 'TA-Lib' | Out-File $LogFilePath -Append 2>&1 + +# Present options for requirement files +$selectedIndices = Get-UserSelection -prompt "Select which requirement files to install:" -options $RequirementFiles -defaultChoice 'A' + +# Install selected requirement files +foreach ($index in $selectedIndices) { + if ($index -lt 0 -or $index -ge $RequirementFiles.Length) { + Write-Log "Invalid selection index: $index" -Level 'ERROR' + continue + } + + $filePath = Join-Path $ProjectDir $RequirementFiles[$index] + if (Test-Path $filePath) { + Install-And-Resolve $filePath + } + else { + Write-Log "Requirement file not found: $filePath" -Level 'ERROR' + Exit-Script -exitCode 1 + } +} + +# Install freqtrade from setup using the virtual environment's Python +Write-Log "Installing freqtrade from setup..." +$setupInstallCommand = "$VenvPython -m pip install -e ." +Invoke-Expression $setupInstallCommand | Out-File $LogFilePath -Append 2>&1 +if ($LASTEXITCODE -ne 0) { + Write-Log "Failed to install freqtrade." -Level 'ERROR' + Exit-Script -exitCode 1 +} + +# Ask if the user wants to install the UI +$uiOptions = @("Yes", "No") +$installUI = Get-UserSelection -prompt "Do you want to install the freqtrade UI?" -options $uiOptions -defaultChoice 'B' + +if ($uiOptions[$installUI] -eq "Yes") { + # Install freqtrade UI using the virtual environment's install-ui command + Write-Log "Installing freqtrade UI..." + & $VenvPython 'freqtrade', 'install-ui' | Out-File $LogFilePath -Append 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Log "Failed to install freqtrade UI." -Level 'ERROR' + Exit-Script -exitCode 1 + } +} + +Write-Log "Update complete!" +Exit-Script -exitCode 0