enviroment-check.ps1 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. # Environment check — portable nodejs/node, python/py, root node_modules
  2. [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
  3. $OutputEncoding = [System.Text.Encoding]::UTF8
  4. chcp 65001 | Out-Null
  5. $scriptRoot = $PSScriptRoot
  6. if (-not $scriptRoot) { $scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path }
  7. if (-not $scriptRoot) { $scriptRoot = (Get-Location).Path }
  8. # Mirror source dir to dest (exFAT-safe; avoids Remove-Item on deep npm trees with junctions)
  9. function Sync-DirRobocopyMirror {
  10. param(
  11. [string]$SourceDir,
  12. [string]$DestDir
  13. )
  14. if (-not (Test-Path -LiteralPath $SourceDir)) { return $false }
  15. New-Item -ItemType Directory -Force -Path $DestDir | Out-Null
  16. & robocopy.exe $SourceDir $DestDir /MIR /R:2 /W:1 /NFL /NDL /NJH /NJS /nc /ns /np | Out-Null
  17. $rc = $LASTEXITCODE
  18. return ($rc -lt 8)
  19. }
  20. # exFAT: Git dubious ownership
  21. $repoGitDir = Join-Path $scriptRoot '.git'
  22. if ((Test-Path $repoGitDir) -and (Get-Command git -ErrorAction SilentlyContinue)) {
  23. $driveLetter = $scriptRoot.Substring(0, 2)
  24. if ($driveLetter -match '^[A-Za-z]:$') {
  25. $volInfo = & cmd.exe /c "fsutil fsinfo volumeinfo $driveLetter 2>nul" | Out-String
  26. if ($volInfo -match 'exFAT') {
  27. $rootForGit = ($scriptRoot.TrimEnd('\')) -replace '\\', '/'
  28. $listed = @(git config --global --get-all safe.directory 2>$null)
  29. $gitSafeListed = $false
  30. foreach ($entry in $listed) {
  31. if ($entry -eq $rootForGit) { $gitSafeListed = $true; break }
  32. }
  33. if (-not $gitSafeListed) {
  34. Write-Host '[exFAT] Git: registering safe.directory...' -ForegroundColor Yellow
  35. & git config --global --add safe.directory $rootForGit
  36. }
  37. }
  38. }
  39. }
  40. $nodeExeBootstrap = Join-Path $scriptRoot 'nodejs\node\node.exe'
  41. $configJs = Join-Path $scriptRoot 'config.js'
  42. if ((Test-Path $nodeExeBootstrap) -and (Test-Path $configJs)) {
  43. Push-Location $scriptRoot
  44. $cfgJson = & $nodeExeBootstrap '-e' "const c=require('./config.js');console.log(JSON.stringify({nodeDir:c.nodeDir,npmCmdPath:c.npmCmdPath,npmCliPath:c.npmCliPath,pythonDir:c.pythonDir||(c.pythonPath&&c.pythonPath.path)}))" 2>$null
  45. Pop-Location
  46. if ($cfgJson) {
  47. $cfg = $cfgJson | ConvertFrom-Json
  48. $nodeDir = $cfg.nodeDir
  49. $npmCmd = $cfg.npmCmdPath
  50. $npmCliPath = $cfg.npmCliPath
  51. if ($cfg.pythonDir) { $pythonEmbedRoot = $cfg.pythonDir }
  52. }
  53. }
  54. if (-not $nodeDir) { $nodeDir = Join-Path $scriptRoot 'nodejs\node' }
  55. if (-not $npmCliPath) { $npmCliPath = Join-Path $nodeDir 'node_modules\npm\bin\npm-cli.js' }
  56. if (-not $npmCmd) { $npmCmd = Join-Path $nodeDir 'npm.cmd' }
  57. $nodeExe = Join-Path $nodeDir 'node.exe'
  58. if (-not (Test-Path $nodeExe)) { $nodeExe = $null }
  59. if (-not (Test-Path $npmCmd)) { $npmCmd = $null }
  60. if (-not $pythonEmbedRoot) { $pythonEmbedRoot = Join-Path $scriptRoot 'python\py' }
  61. $pythonExe = Join-Path $pythonEmbedRoot 'python.exe'
  62. if (-not (Test-Path $pythonExe)) { $pythonExe = Join-Path $pythonEmbedRoot 'python' }
  63. Write-Host ''
  64. Write-Host 'Checking development environment...' -ForegroundColor Cyan
  65. Write-Host '================================' -ForegroundColor Cyan
  66. Write-Host ''
  67. Write-Host 'Checking Node.js (nodejs/node)...' -ForegroundColor Yellow
  68. if (-not $nodeExe) {
  69. Write-Host ('[X] Local Node not found. Put Node in: ' + $nodeDir) -ForegroundColor Red
  70. exit 1
  71. }
  72. $nodeVersion = & $nodeExe --version 2>$null
  73. if (-not $nodeVersion) {
  74. Write-Host '[X] Local node.exe failed' -ForegroundColor Red
  75. exit 1
  76. }
  77. Write-Host ('[OK] Node.js: ' + $nodeVersion) -ForegroundColor Green
  78. Write-Host ''
  79. Write-Host 'Checking npm (local)...' -ForegroundColor Yellow
  80. $npmCliPath = Join-Path $nodeDir 'node_modules\npm\bin\npm-cli.js'
  81. $npmCmd = Join-Path $nodeDir 'npm.cmd'
  82. if (-not (Test-Path $npmCmd)) { $npmCmd = $null }
  83. $nodeModulesPath = Join-Path $nodeDir 'node_modules'
  84. $npmBackupDir = Join-Path $scriptRoot 'nodejs\backup'
  85. $npmBackupNpmDir = Join-Path $npmBackupDir 'npm'
  86. New-Item -ItemType Directory -Force -Path $npmBackupDir | Out-Null
  87. $npmHealthy = $false
  88. if ((Test-Path $npmCliPath) -and $npmCmd) {
  89. $npmVersion = & $npmCmd --version 2>$null
  90. if ($npmVersion) {
  91. $npmHealthy = $true
  92. Write-Host ('[OK] npm: ' + $npmVersion) -ForegroundColor Green
  93. }
  94. }
  95. if (-not $npmHealthy) {
  96. Write-Host '[WARN] npm missing or broken; restoring from nodejs\backup\npm (robocopy)...' -ForegroundColor Yellow
  97. if (Test-Path $npmBackupNpmDir) {
  98. New-Item -ItemType Directory -Force -Path $nodeModulesPath | Out-Null
  99. $nodeNpmDir = Join-Path $nodeModulesPath 'npm'
  100. if (-not (Sync-DirRobocopyMirror -SourceDir $npmBackupNpmDir -DestDir $nodeNpmDir)) {
  101. Write-Host '[WARN] npm restore from nodejs\backup\npm failed (robocopy exit >= 8 or locks)' -ForegroundColor Yellow
  102. }
  103. if ((Test-Path $npmCliPath) -and $npmCmd) {
  104. $npmVersion = & $npmCmd --version 2>$null
  105. if ($npmVersion) { $npmHealthy = $true; Write-Host ('[OK] npm restored from backup: ' + $npmVersion) -ForegroundColor Green }
  106. }
  107. }
  108. }
  109. if (-not $npmHealthy) {
  110. $installNpmBat = Join-Path $nodeDir 'install-npm.bat'
  111. if (-not (Test-Path $installNpmBat)) {
  112. Write-Host ('[X] Run install-npm.bat in nodejs\node') -ForegroundColor Red
  113. exit 1
  114. }
  115. Write-Host 'Running install-npm.bat...' -ForegroundColor Yellow
  116. cmd /c "`"$installNpmBat`" /q"
  117. if ($LASTEXITCODE -ne 0) { Write-Host '[X] npm installation failed' -ForegroundColor Red; exit 1 }
  118. if (-not (Test-Path $npmCliPath)) { Write-Host '[X] npm-cli.js still missing' -ForegroundColor Red; exit 1 }
  119. $npmHealthy = $true
  120. Write-Host '[OK] npm installed' -ForegroundColor Green
  121. }
  122. if ($npmHealthy -and (Test-Path (Join-Path $nodeModulesPath 'npm'))) {
  123. $liveNpmDir = Join-Path $nodeModulesPath 'npm'
  124. if (Sync-DirRobocopyMirror -SourceDir $liveNpmDir -DestDir $npmBackupNpmDir) {
  125. Write-Host ('[OK] npm backup (robocopy mirror): ' + $npmBackupNpmDir) -ForegroundColor Green
  126. } else {
  127. Write-Host '[WARN] npm backup sync failed (robocopy exit >= 8); path or file locks?' -ForegroundColor Yellow
  128. }
  129. }
  130. if ($npmCmd) {
  131. Push-Location $scriptRoot
  132. & $npmCmd config set registry https://registry.npmmirror.com 2>$null
  133. Pop-Location
  134. }
  135. Write-Host ''
  136. Write-Host 'Project root npm dependencies...' -ForegroundColor Yellow
  137. $rootVite = Join-Path $scriptRoot 'node_modules\vite\package.json'
  138. if (-not (Test-Path $rootVite)) {
  139. Write-Host 'Running npm install at repo root...' -ForegroundColor Yellow
  140. Push-Location $scriptRoot
  141. & $npmCmd install --no-audit --no-fund --loglevel=error
  142. Pop-Location
  143. if (-not (Test-Path $rootVite)) {
  144. Write-Host '[X] Root npm install did not install vite' -ForegroundColor Red
  145. exit 1
  146. }
  147. }
  148. Write-Host '[OK] Root node_modules ready' -ForegroundColor Green
  149. if (-not (Test-Path $npmCliPath)) {
  150. Write-Host '[X] npm-cli.js missing after install' -ForegroundColor Red
  151. exit 1
  152. }
  153. Write-Host ''
  154. Write-Host 'Checking Python (python/py)...' -ForegroundColor Yellow
  155. if (-not (Test-Path $pythonExe)) {
  156. Write-Host ('[X] Python not found: ' + $pythonExe) -ForegroundColor Red
  157. exit 1
  158. }
  159. Write-Host '[OK] python found' -ForegroundColor Green
  160. Write-Host ''
  161. Write-Host 'Embedded Python toolchain (python/backup)...' -ForegroundColor Yellow
  162. $embSitePackages = Join-Path $pythonEmbedRoot 'Lib\site-packages'
  163. $bakSitePackages = Join-Path $scriptRoot 'python\backup\site-packages'
  164. $pypiIndexUrl = 'https://pypi.tuna.tsinghua.edu.cn/simple'
  165. $pypiTrustedHost = 'pypi.tuna.tsinghua.edu.cn'
  166. New-Item -ItemType Directory -Force -Path (Join-Path $scriptRoot 'python\backup') | Out-Null
  167. New-Item -ItemType Directory -Force -Path $bakSitePackages | Out-Null
  168. $requiredPipFiles = @(
  169. (Join-Path $embSitePackages 'pip\__main__.py'),
  170. (Join-Path $embSitePackages 'pip\_internal\cli\main.py')
  171. )
  172. $pipOk = @($requiredPipFiles | Where-Object { -not (Test-Path $_) }).Count -eq 0
  173. if (-not $pipOk) {
  174. Write-Host 'Restoring pip from python\backup\site-packages...' -ForegroundColor Yellow
  175. Get-ChildItem -LiteralPath $bakSitePackages -ErrorAction SilentlyContinue | ForEach-Object {
  176. Copy-Item -LiteralPath $_.FullName -Destination $embSitePackages -Recurse -Force
  177. }
  178. $pipOk = @($requiredPipFiles | Where-Object { -not (Test-Path $_) }).Count -eq 0
  179. }
  180. $getPipScript = Join-Path $scriptRoot 'python\get-pip.py'
  181. if (-not $pipOk -and (Test-Path $getPipScript)) {
  182. & $pythonExe $getPipScript --force-reinstall -i $pypiIndexUrl --trusted-host $pypiTrustedHost --no-warn-script-location
  183. $pipOk = @($requiredPipFiles | Where-Object { -not (Test-Path $_) }).Count -eq 0
  184. }
  185. if ($pipOk) {
  186. Write-Host '[OK] pip ready' -ForegroundColor Green
  187. } else {
  188. Write-Host '[WARN] pip may be incomplete; place get-pip.py under python/' -ForegroundColor Yellow
  189. }
  190. Write-Host ''
  191. Write-Host 'Python packages (python/python-enviroment-install.py)...' -ForegroundColor Yellow
  192. $pyInstallScript = Join-Path $scriptRoot 'python\python-enviroment-install.py'
  193. if (-not (Test-Path $pyInstallScript)) {
  194. Write-Host ('[X] Missing ' + $pyInstallScript) -ForegroundColor Red
  195. exit 1
  196. }
  197. & $pythonExe $pyInstallScript
  198. if ($LASTEXITCODE -ne 0) {
  199. Write-Host '[X] Python dependency install failed' -ForegroundColor Red
  200. exit 1
  201. }
  202. Write-Host ''
  203. Write-Host '================================' -ForegroundColor Cyan
  204. Write-Host 'Environment check completed!' -ForegroundColor Green