117 lines
4.0 KiB
PowerShell
117 lines
4.0 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[string]$NodeVersion = '20.20.2',
|
|
[int]$MaxOldSpaceSizeMB = 8192
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
|
|
$toolRoot = Join-Path $env:LOCALAPPDATA 'CaiWuXiTong\tools'
|
|
$nodeFolderName = "node-v$NodeVersion-win-x64"
|
|
$nodeRoot = Join-Path $toolRoot $nodeFolderName
|
|
$nodeExe = Join-Path $nodeRoot 'node.exe'
|
|
$npmCmd = Join-Path $nodeRoot 'npm.cmd'
|
|
$archiveName = "$nodeFolderName.zip"
|
|
$archivePath = Join-Path $toolRoot $archiveName
|
|
$checksumPath = Join-Path $toolRoot 'SHASUMS256.txt'
|
|
$trackedCssPath = Join-Path $repoRoot 'public/css/app.css'
|
|
$frontendOutputRoot = Join-Path $repoRoot '.caiwu-build/public'
|
|
|
|
function Invoke-Native {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[scriptblock]$Command,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$FailureMessage
|
|
)
|
|
|
|
& $Command
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw $FailureMessage
|
|
}
|
|
}
|
|
|
|
if (-not (Test-Path -LiteralPath $nodeExe)) {
|
|
New-Item -ItemType Directory -Force -Path $toolRoot | Out-Null
|
|
|
|
try {
|
|
$downloadRoot = "https://nodejs.org/dist/v$NodeVersion"
|
|
Invoke-WebRequest -Uri "$downloadRoot/$archiveName" -OutFile $archivePath
|
|
Invoke-WebRequest -Uri "$downloadRoot/SHASUMS256.txt" -OutFile $checksumPath
|
|
|
|
$checksumLine = Select-String -Path $checksumPath -Pattern ([regex]::Escape($archiveName) + '$') |
|
|
Select-Object -First 1
|
|
if (-not $checksumLine) {
|
|
throw 'Node 安装包缺少官方 SHA-256 校验值。'
|
|
}
|
|
|
|
$expectedHash = ($checksumLine.Line -split '\s+')[0].ToLowerInvariant()
|
|
$actualHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $archivePath).Hash.ToLowerInvariant()
|
|
if ($actualHash -ne $expectedHash) {
|
|
throw 'Node 安装包 SHA-256 校验失败。'
|
|
}
|
|
|
|
Expand-Archive -LiteralPath $archivePath -DestinationPath $toolRoot -Force
|
|
}
|
|
finally {
|
|
if (Test-Path -LiteralPath $archivePath) {
|
|
Remove-Item -LiteralPath $archivePath -Force
|
|
}
|
|
if (Test-Path -LiteralPath $checksumPath) {
|
|
Remove-Item -LiteralPath $checksumPath -Force
|
|
}
|
|
}
|
|
}
|
|
|
|
if (-not (Test-Path -LiteralPath $nodeExe) -or -not (Test-Path -LiteralPath $npmCmd)) {
|
|
throw "项目专用 Node $NodeVersion 安装不完整。"
|
|
}
|
|
|
|
$originalPath = $env:Path
|
|
$originalNodeOptions = $env:NODE_OPTIONS
|
|
$trackedCssBackup = if (Test-Path -LiteralPath $trackedCssPath) {
|
|
[IO.File]::ReadAllBytes($trackedCssPath)
|
|
}
|
|
else {
|
|
$null
|
|
}
|
|
|
|
Push-Location $repoRoot
|
|
try {
|
|
$env:Path = "$nodeRoot;$originalPath"
|
|
$env:NODE_OPTIONS = "--max-old-space-size=$MaxOldSpaceSizeMB"
|
|
|
|
Invoke-Native { & $nodeExe --version } '项目专用 Node 无法运行。'
|
|
Invoke-Native {
|
|
& $npmCmd ci --prefer-offline --no-audit --no-fund
|
|
} '前端依赖安装失败。'
|
|
Invoke-Native { & $npmCmd run production } '前端资源构建失败。'
|
|
|
|
$requiredOutputs = @(
|
|
'public/js/auth/common.min.js',
|
|
'public/css/app.css',
|
|
'public/mix-manifest.json'
|
|
)
|
|
foreach ($output in $requiredOutputs) {
|
|
if (-not (Test-Path -LiteralPath (Join-Path $repoRoot $output))) {
|
|
throw "前端构建缺少产物:$output"
|
|
}
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $frontendOutputRoot) {
|
|
Remove-Item -LiteralPath $frontendOutputRoot -Recurse -Force
|
|
}
|
|
New-Item -ItemType Directory -Force -Path (Join-Path $frontendOutputRoot 'css') | Out-Null
|
|
Copy-Item -LiteralPath (Join-Path $repoRoot 'public/js') -Destination $frontendOutputRoot -Recurse
|
|
Copy-Item -LiteralPath (Join-Path $repoRoot 'public/css/app.css') -Destination (Join-Path $frontendOutputRoot 'css/app.css')
|
|
Copy-Item -LiteralPath (Join-Path $repoRoot 'public/mix-manifest.json') -Destination $frontendOutputRoot
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
$env:Path = $originalPath
|
|
$env:NODE_OPTIONS = $originalNodeOptions
|
|
if ($null -ne $trackedCssBackup) {
|
|
[IO.File]::WriteAllBytes($trackedCssPath, $trackedCssBackup)
|
|
}
|
|
}
|