You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

153 lines
6.3 KiB

@echo off
setlocal enabledelayedexpansion
:: Qoder CLI Installer for Windows CMD
:: Usage: curl -fsSL https://qoder-ide.oss-accelerate.aliyuncs.com/qodercli/install.cmd -o install.cmd && install.cmd && del install.cmd
::
:: Responsibilities (script layer):
:: - Manifest fetch, download, SHA256 verification, extraction
:: Post-extraction, delegates to binary's `install` subcommand for:
:: - Versioned placement, entry-point, PATH config, marker, verification, telemetry
set "BASE_URL=https://qoder-ide.oss-accelerate.aliyuncs.com/qodercli"
set "BINARY_NAME=qodercli"
:: Check for required tools
where curl >nul 2>&1 || (
echo Error: curl is required but not found.
echo Use the PowerShell installer instead:
echo irm %BASE_URL%/install.ps1 ^| iex
exit /b 1
)
:: Create temp directory with exec permission
call :create_exec_tmpdir
if errorlevel 1 (
echo Error: Cannot create temporary directory with exec permission
exit /b 1
)
:: 1. Download manifest
echo ==> Fetching release information...
curl -fsSL "%BASE_URL%/channels/manifest.json" -o "%TEMP_DIR%\manifest.json"
if errorlevel 1 (
echo Error: Failed to download manifest
goto :cleanup_fail
)
:: 2. Parse version
set "VERSION="
for /f "usebackq tokens=2 delims=:," %%a in (`findstr /c:"\"latest\"" "%TEMP_DIR%\manifest.json"`) do (
set "VERSION=%%~a"
)
set "VERSION=%VERSION: =%"
set "VERSION=%VERSION:"=%"
if "%VERSION%"=="" (
echo Error: Failed to parse version from manifest
goto :cleanup_fail
)
echo ==> Latest version: %VERSION%
:: 3. Parse download URL and checksum
set "DOWNLOAD_URL="
set "EXPECTED_SHA256="
powershell -NoProfile -Command "$m = Get-Content '%TEMP_DIR%\manifest.json' | ConvertFrom-Json; $build = [Environment]::OSVersion.Version.Build; $items = @($m.files | Where-Object { $_.os -eq 'windows' -and ($_.arch -eq 'amd64' -or $_.arch -eq 'x64') }); function Test-WindowsAvx2Available { try { $dq = [char]34; $sig = '[System.Runtime.InteropServices.DllImport(' + $dq + 'kernel32.dll' + $dq + ', SetLastError=false)] public static extern bool IsProcessorFeaturePresent(uint ProcessorFeature);'; if (-not ('QoderCli.ProcessorFeature' -as [type])) { Add-Type -Namespace QoderCli -Name ProcessorFeature -MemberDefinition $sig -ErrorAction Stop }; return [QoderCli.ProcessorFeature]::IsProcessorFeaturePresent(40) } catch { return $null } }; $needsLegacyForBuild = $build -gt 0 -and $build -lt 17763; $avx2 = $null; if (-not $needsLegacyForBuild) { $avx2 = Test-WindowsAvx2Available }; $needsLegacy = $needsLegacyForBuild -or ($avx2 -eq $false); function Compat($x, $enforceMax) { if ($build -le 0) { return $true }; if ($x.min_windows_build -and $build -lt [int]$x.min_windows_build) { return $false }; if ($enforceMax -and $x.max_windows_build -and $build -gt [int]$x.max_windows_build) { return $false }; return $true }; $f = $null; if ($needsLegacy) { $f = $items | Where-Object { $_.variant -eq 'legacy' -or $_.runtime -eq 'node-sea' } | Where-Object { Compat $_ $needsLegacyForBuild } | Select-Object -First 1; if (-not $f) { exit 2 } } else { $f = $items | Where-Object { $_.variant -ne 'legacy' -and $_.runtime -ne 'node-sea' } | Where-Object { Compat $_ $true } | Select-Object -First 1; if (-not $f) { $f = $items | Where-Object { Compat $_ $true } | Select-Object -First 1 } }; if (-not $f) { exit 3 }; Set-Content -Path '%TEMP_DIR%\selected.txt' -Value @($f.url, $f.sha256)"
if errorlevel 3 (
echo Error: No compatible binary for windows/x64
goto :cleanup_fail
)
if errorlevel 2 (
echo Error: No legacy Windows binary found for this Windows build/CPU
goto :cleanup_fail
)
set /a SELECTED_LINE=0
for /f "usebackq delims=" %%l in ("%TEMP_DIR%\selected.txt") do (
set /a SELECTED_LINE+=1
if "!SELECTED_LINE!"=="1" set "DOWNLOAD_URL=%%l"
if "!SELECTED_LINE!"=="2" set "EXPECTED_SHA256=%%l"
)
if "%DOWNLOAD_URL%"=="" (
echo Error: Failed to parse download URL
goto :cleanup_fail
)
:: 4. Download
echo ==> Downloading %BINARY_NAME% %VERSION%...
echo %DOWNLOAD_URL% | findstr /b /c:"https://" >nul 2>&1
if errorlevel 1 (
echo Error: Insecure URL rejected ^(must use HTTPS^)
goto :cleanup_fail
)
curl -fSL --progress-bar "%DOWNLOAD_URL%" -o "%TEMP_DIR%\%BINARY_NAME%.zip"
if errorlevel 1 (
echo Error: Failed to download binary
goto :cleanup_fail
)
:: 5. Verify SHA256
if not "%EXPECTED_SHA256%"=="" (
echo ==> Verifying checksum...
for /f "usebackq delims=" %%h in (`powershell -NoProfile -Command "(Get-FileHash '%TEMP_DIR%\%BINARY_NAME%.zip' -Algorithm SHA256).Hash.ToLower()"`) do set "ACTUAL_SHA256=%%h"
if /i not "!ACTUAL_SHA256!"=="%EXPECTED_SHA256%" (
echo Error: Checksum mismatch
echo Expected: %EXPECTED_SHA256%
echo Actual: !ACTUAL_SHA256!
goto :cleanup_fail
)
echo ==> Checksum verified
)
:: 6. Extract
echo ==> Extracting...
powershell -NoProfile -Command "Expand-Archive -Path '%TEMP_DIR%\%BINARY_NAME%.zip' -DestinationPath '%TEMP_DIR%' -Force"
if errorlevel 1 (
echo Error: Failed to extract archive
goto :cleanup_fail
)
if not exist "%TEMP_DIR%\%BINARY_NAME%.exe" (
echo Error: %BINARY_NAME%.exe not found in archive
goto :cleanup_fail
)
:: =========================================================================
:: 7. Delegate to binary's install subcommand
:: =========================================================================
echo ==> Installing...
"%TEMP_DIR%\%BINARY_NAME%.exe" install --force
if errorlevel 1 (
echo Error: %BINARY_NAME% install failed
goto :cleanup_fail
)
:cleanup_success
rmdir /s /q "%TEMP_DIR%" 2>nul
echo.
echo %BINARY_NAME% %VERSION% installed successfully!
echo.
echo Please restart your terminal, then run: %BINARY_NAME% --help
echo.
exit /b 0
:cleanup_fail
rmdir /s /q "%TEMP_DIR%" 2>nul
exit /b 1
:: =========================================================================
:: create_exec_tmpdir — create a temporary working directory
:: =========================================================================
:create_exec_tmpdir
set "TEMP_DIR=%TEMP%\%BINARY_NAME%-install-%RANDOM%"
mkdir "%TEMP_DIR%" 2>nul
if not errorlevel 1 exit /b 0
:: Fallback to user-local dir if TEMP fails
set "FALLBACK_BASE=%USERPROFILE%\.qoder\tmp"
if not exist "%FALLBACK_BASE%" mkdir "%FALLBACK_BASE%" 2>nul
set "TEMP_DIR=%FALLBACK_BASE%\%BINARY_NAME%-install-%RANDOM%"
mkdir "%TEMP_DIR%" 2>nul
if errorlevel 1 exit /b 1
exit /b 0