| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- # Environment check — portable nodejs/node, python/py, root node_modules
- [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
- $OutputEncoding = [System.Text.Encoding]::UTF8
- chcp 65001 | Out-Null
- $scriptRoot = $PSScriptRoot
- if (-not $scriptRoot) { $scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path }
- if (-not $scriptRoot) { $scriptRoot = (Get-Location).Path }
- # Mirror source dir to dest (exFAT-safe; avoids Remove-Item on deep npm trees with junctions)
- function Sync-DirRobocopyMirror {
- param(
- [string]$SourceDir,
- [string]$DestDir
- )
- if (-not (Test-Path -LiteralPath $SourceDir)) { return $false }
- New-Item -ItemType Directory -Force -Path $DestDir | Out-Null
- & robocopy.exe $SourceDir $DestDir /MIR /R:2 /W:1 /NFL /NDL /NJH /NJS /nc /ns /np | Out-Null
- $rc = $LASTEXITCODE
- return ($rc -lt 8)
- }
- # exFAT: Git dubious ownership
- $repoGitDir = Join-Path $scriptRoot '.git'
- if ((Test-Path $repoGitDir) -and (Get-Command git -ErrorAction SilentlyContinue)) {
- $driveLetter = $scriptRoot.Substring(0, 2)
- if ($driveLetter -match '^[A-Za-z]:$') {
- $volInfo = & cmd.exe /c "fsutil fsinfo volumeinfo $driveLetter 2>nul" | Out-String
- if ($volInfo -match 'exFAT') {
- $rootForGit = ($scriptRoot.TrimEnd('\')) -replace '\\', '/'
- $listed = @(git config --global --get-all safe.directory 2>$null)
- $gitSafeListed = $false
- foreach ($entry in $listed) {
- if ($entry -eq $rootForGit) { $gitSafeListed = $true; break }
- }
- if (-not $gitSafeListed) {
- Write-Host '[exFAT] Git: registering safe.directory...' -ForegroundColor Yellow
- & git config --global --add safe.directory $rootForGit
- }
- }
- }
- }
- $nodeExeBootstrap = Join-Path $scriptRoot 'nodejs\node\node.exe'
- $configJs = Join-Path $scriptRoot 'config.js'
- if ((Test-Path $nodeExeBootstrap) -and (Test-Path $configJs)) {
- Push-Location $scriptRoot
- $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
- Pop-Location
- if ($cfgJson) {
- $cfg = $cfgJson | ConvertFrom-Json
- $nodeDir = $cfg.nodeDir
- $npmCmd = $cfg.npmCmdPath
- $npmCliPath = $cfg.npmCliPath
- if ($cfg.pythonDir) { $pythonEmbedRoot = $cfg.pythonDir }
- }
- }
- if (-not $nodeDir) { $nodeDir = Join-Path $scriptRoot 'nodejs\node' }
- if (-not $npmCliPath) { $npmCliPath = Join-Path $nodeDir 'node_modules\npm\bin\npm-cli.js' }
- if (-not $npmCmd) { $npmCmd = Join-Path $nodeDir 'npm.cmd' }
- $nodeExe = Join-Path $nodeDir 'node.exe'
- if (-not (Test-Path $nodeExe)) { $nodeExe = $null }
- if (-not (Test-Path $npmCmd)) { $npmCmd = $null }
- if (-not $pythonEmbedRoot) { $pythonEmbedRoot = Join-Path $scriptRoot 'python\py' }
- $pythonExe = Join-Path $pythonEmbedRoot 'python.exe'
- if (-not (Test-Path $pythonExe)) { $pythonExe = Join-Path $pythonEmbedRoot 'python' }
- Write-Host ''
- Write-Host 'Checking development environment...' -ForegroundColor Cyan
- Write-Host '================================' -ForegroundColor Cyan
- Write-Host ''
- Write-Host 'Checking Node.js (nodejs/node)...' -ForegroundColor Yellow
- if (-not $nodeExe) {
- Write-Host ('[X] Local Node not found. Put Node in: ' + $nodeDir) -ForegroundColor Red
- exit 1
- }
- $nodeVersion = & $nodeExe --version 2>$null
- if (-not $nodeVersion) {
- Write-Host '[X] Local node.exe failed' -ForegroundColor Red
- exit 1
- }
- Write-Host ('[OK] Node.js: ' + $nodeVersion) -ForegroundColor Green
- Write-Host ''
- Write-Host 'Checking npm (local)...' -ForegroundColor Yellow
- $npmCliPath = Join-Path $nodeDir 'node_modules\npm\bin\npm-cli.js'
- $npmCmd = Join-Path $nodeDir 'npm.cmd'
- if (-not (Test-Path $npmCmd)) { $npmCmd = $null }
- $nodeModulesPath = Join-Path $nodeDir 'node_modules'
- $npmBackupDir = Join-Path $scriptRoot 'nodejs\backup'
- $npmBackupNpmDir = Join-Path $npmBackupDir 'npm'
- New-Item -ItemType Directory -Force -Path $npmBackupDir | Out-Null
- $npmHealthy = $false
- if ((Test-Path $npmCliPath) -and $npmCmd) {
- $npmVersion = & $npmCmd --version 2>$null
- if ($npmVersion) {
- $npmHealthy = $true
- Write-Host ('[OK] npm: ' + $npmVersion) -ForegroundColor Green
- }
- }
- if (-not $npmHealthy) {
- Write-Host '[WARN] npm missing or broken; restoring from nodejs\backup\npm (robocopy)...' -ForegroundColor Yellow
- if (Test-Path $npmBackupNpmDir) {
- New-Item -ItemType Directory -Force -Path $nodeModulesPath | Out-Null
- $nodeNpmDir = Join-Path $nodeModulesPath 'npm'
- if (-not (Sync-DirRobocopyMirror -SourceDir $npmBackupNpmDir -DestDir $nodeNpmDir)) {
- Write-Host '[WARN] npm restore from nodejs\backup\npm failed (robocopy exit >= 8 or locks)' -ForegroundColor Yellow
- }
- if ((Test-Path $npmCliPath) -and $npmCmd) {
- $npmVersion = & $npmCmd --version 2>$null
- if ($npmVersion) { $npmHealthy = $true; Write-Host ('[OK] npm restored from backup: ' + $npmVersion) -ForegroundColor Green }
- }
- }
- }
- if (-not $npmHealthy) {
- $installNpmBat = Join-Path $nodeDir 'install-npm.bat'
- if (-not (Test-Path $installNpmBat)) {
- Write-Host ('[X] Run install-npm.bat in nodejs\node') -ForegroundColor Red
- exit 1
- }
- Write-Host 'Running install-npm.bat...' -ForegroundColor Yellow
- cmd /c "`"$installNpmBat`" /q"
- if ($LASTEXITCODE -ne 0) { Write-Host '[X] npm installation failed' -ForegroundColor Red; exit 1 }
- if (-not (Test-Path $npmCliPath)) { Write-Host '[X] npm-cli.js still missing' -ForegroundColor Red; exit 1 }
- $npmHealthy = $true
- Write-Host '[OK] npm installed' -ForegroundColor Green
- }
- if ($npmHealthy -and (Test-Path (Join-Path $nodeModulesPath 'npm'))) {
- $liveNpmDir = Join-Path $nodeModulesPath 'npm'
- if (Sync-DirRobocopyMirror -SourceDir $liveNpmDir -DestDir $npmBackupNpmDir) {
- Write-Host ('[OK] npm backup (robocopy mirror): ' + $npmBackupNpmDir) -ForegroundColor Green
- } else {
- Write-Host '[WARN] npm backup sync failed (robocopy exit >= 8); path or file locks?' -ForegroundColor Yellow
- }
- }
- if ($npmCmd) {
- Push-Location $scriptRoot
- & $npmCmd config set registry https://registry.npmmirror.com 2>$null
- Pop-Location
- }
- Write-Host ''
- Write-Host 'Project root npm dependencies...' -ForegroundColor Yellow
- $rootVite = Join-Path $scriptRoot 'node_modules\vite\package.json'
- if (-not (Test-Path $rootVite)) {
- Write-Host 'Running npm install at repo root...' -ForegroundColor Yellow
- Push-Location $scriptRoot
- $npmInstallOut = @(& $npmCmd install --no-audit --no-fund --loglevel=error 2>&1 | ForEach-Object { "$_" })
- $npmInstallExit = $LASTEXITCODE
- Pop-Location
- $installLogText = $npmInstallOut -join "`n"
- $npmBundleBroken = ($installLogText -match 'Cannot find module') -and (
- $installLogText -match 'node_modules[\\/]npm[\\/]' -or $installLogText -match 'graceful-fs'
- )
- $retryInstallOut = $null
- if ((-not (Test-Path $rootVite)) -and $npmBundleBroken) {
- $bootstrapJs = Join-Path $scriptRoot 'nodejs\bootstrap-npm.js'
- Write-Host 'Local npm is missing built-in modules; repairing via nodejs/bootstrap-npm.js (backup first, then registry if needed)...' -ForegroundColor Yellow
- if (-not (Test-Path $bootstrapJs)) {
- Write-Host '[X] Missing nodejs\bootstrap-npm.js; cannot repair npm' -ForegroundColor Red
- exit 1
- }
- & $nodeExe $bootstrapJs
- if ($LASTEXITCODE -ne 0) {
- Write-Host '[X] npm bootstrap (reinstall) failed' -ForegroundColor Red
- exit 1
- }
- if (-not (Test-Path $npmCliPath)) {
- Write-Host '[X] npm-cli.js still missing after bootstrap' -ForegroundColor Red
- exit 1
- }
- Push-Location $scriptRoot
- & $npmCmd config set registry https://registry.npmmirror.com 2>$null
- $retryInstallOut = @(& $npmCmd install --no-audit --no-fund --loglevel=error 2>&1 | ForEach-Object { "$_" })
- Pop-Location
- $liveNpmDir = Join-Path $nodeModulesPath 'npm'
- if ((Test-Path $liveNpmDir) -and (Sync-DirRobocopyMirror -SourceDir $liveNpmDir -DestDir $npmBackupNpmDir)) {
- Write-Host ('[OK] npm backup (robocopy mirror): ' + $npmBackupNpmDir) -ForegroundColor Green
- }
- }
- if (-not (Test-Path $rootVite)) {
- if ($retryInstallOut) {
- Write-Host ($retryInstallOut -join "`n")
- } elseif ($npmInstallExit -ne 0 -and $installLogText) {
- Write-Host $installLogText
- }
- Write-Host '[X] Root npm install did not install vite' -ForegroundColor Red
- exit 1
- }
- }
- Write-Host '[OK] Root node_modules ready' -ForegroundColor Green
- if (-not (Test-Path $npmCliPath)) {
- Write-Host '[X] npm-cli.js missing after install' -ForegroundColor Red
- exit 1
- }
- Write-Host ''
- Write-Host 'Checking Python (python/py)...' -ForegroundColor Yellow
- if (-not (Test-Path $pythonExe)) {
- Write-Host ('[X] Python not found: ' + $pythonExe) -ForegroundColor Red
- exit 1
- }
- Write-Host '[OK] python found' -ForegroundColor Green
- Write-Host ''
- Write-Host 'Embedded Python toolchain (python/backup)...' -ForegroundColor Yellow
- $embSitePackages = Join-Path $pythonEmbedRoot 'Lib\site-packages'
- $bakSitePackages = Join-Path $scriptRoot 'python\backup\site-packages'
- $pypiIndexUrl = 'https://pypi.tuna.tsinghua.edu.cn/simple'
- $pypiTrustedHost = 'pypi.tuna.tsinghua.edu.cn'
- New-Item -ItemType Directory -Force -Path (Join-Path $scriptRoot 'python\backup') | Out-Null
- New-Item -ItemType Directory -Force -Path $bakSitePackages | Out-Null
- $requiredPipFiles = @(
- (Join-Path $embSitePackages 'pip\__main__.py'),
- (Join-Path $embSitePackages 'pip\_internal\cli\main.py')
- )
- $pipOk = @($requiredPipFiles | Where-Object { -not (Test-Path $_) }).Count -eq 0
- if (-not $pipOk) {
- Write-Host 'Restoring pip from python\backup\site-packages...' -ForegroundColor Yellow
- Get-ChildItem -LiteralPath $bakSitePackages -ErrorAction SilentlyContinue | ForEach-Object {
- Copy-Item -LiteralPath $_.FullName -Destination $embSitePackages -Recurse -Force
- }
- $pipOk = @($requiredPipFiles | Where-Object { -not (Test-Path $_) }).Count -eq 0
- }
- $getPipScript = Join-Path $scriptRoot 'python\get-pip.py'
- if (-not $pipOk -and (Test-Path $getPipScript)) {
- & $pythonExe $getPipScript --force-reinstall -i $pypiIndexUrl --trusted-host $pypiTrustedHost --no-warn-script-location
- $pipOk = @($requiredPipFiles | Where-Object { -not (Test-Path $_) }).Count -eq 0
- }
- if ($pipOk) {
- Write-Host '[OK] pip ready' -ForegroundColor Green
- } else {
- Write-Host '[WARN] pip may be incomplete; place get-pip.py under python/' -ForegroundColor Yellow
- }
- Write-Host ''
- Write-Host 'Python packages (python/python-enviroment-install.py)...' -ForegroundColor Yellow
- $pyInstallScript = Join-Path $scriptRoot 'python\python-enviroment-install.py'
- if (-not (Test-Path $pyInstallScript)) {
- Write-Host ('[X] Missing ' + $pyInstallScript) -ForegroundColor Red
- exit 1
- }
- & $pythonExe $pyInstallScript
- if ($LASTEXITCODE -ne 0) {
- Write-Host '[X] Python dependency install failed' -ForegroundColor Red
- exit 1
- }
- Write-Host ''
- Write-Host '================================' -ForegroundColor Cyan
- Write-Host 'Environment check completed!' -ForegroundColor Green
|