131 lines
6.2 KiB
PowerShell
131 lines
6.2 KiB
PowerShell
param(
|
|
[string]$Destination,
|
|
[string]$NodeVersion = '22.18.0',
|
|
[switch]$Force
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
function Get-Sha256Hex([string]$Path) {
|
|
$stream = [IO.File]::OpenRead($Path)
|
|
$algorithm = [Security.Cryptography.SHA256]::Create()
|
|
try {
|
|
return ([BitConverter]::ToString($algorithm.ComputeHash($stream))).Replace('-', '')
|
|
} finally {
|
|
$algorithm.Dispose()
|
|
$stream.Dispose()
|
|
}
|
|
}
|
|
|
|
$RepoRoot = Split-Path -Parent $PSScriptRoot
|
|
$SourceRoot = Join-Path $RepoRoot 'third_party\MoeKoeMusic'
|
|
$ApiSource = Join-Path $SourceRoot 'api'
|
|
if ([string]::IsNullOrWhiteSpace($Destination)) {
|
|
$Destination = Join-Path $RepoRoot 'build\music-api'
|
|
}
|
|
$Destination = [IO.Path]::GetFullPath($Destination)
|
|
$BuildRoot = [IO.Path]::GetFullPath((Join-Path $RepoRoot 'build'))
|
|
if (-not $Destination.StartsWith($BuildRoot + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) {
|
|
throw "Music API destination must be inside $BuildRoot"
|
|
}
|
|
|
|
$requiredSource = @(
|
|
(Join-Path $SourceRoot 'UPSTREAM_COMMIT'),
|
|
(Join-Path $SourceRoot 'API_SUBMODULE_COMMIT'),
|
|
(Join-Path $SourceRoot 'MOEKOE_LICENSE'),
|
|
(Join-Path $SourceRoot 'MOEKOE_README.md'),
|
|
(Join-Path $ApiSource 'package.json'),
|
|
(Join-Path $ApiSource 'pnpm-lock.yaml'),
|
|
(Join-Path $ApiSource 'LICENSE'),
|
|
(Join-Path $ApiSource 'README.md'),
|
|
(Join-Path $ApiSource 'ymhut-sidecar.js')
|
|
)
|
|
foreach ($path in $requiredSource) {
|
|
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { throw "Required music API source is missing: $path" }
|
|
}
|
|
|
|
$fingerprintLines = Get-ChildItem -LiteralPath $SourceRoot -Recurse -File |
|
|
Where-Object { $_.FullName -notlike '*\node_modules\*' } |
|
|
Sort-Object FullName |
|
|
ForEach-Object {
|
|
$relative = $_.FullName.Substring($SourceRoot.TrimEnd('\').Length + 1).Replace('\', '/')
|
|
"$relative|$(Get-Sha256Hex $_.FullName)"
|
|
}
|
|
$fingerprintBytes = [Text.Encoding]::UTF8.GetBytes(($fingerprintLines -join "`n"))
|
|
$sha256 = [Security.Cryptography.SHA256]::Create()
|
|
try {
|
|
$fingerprint = ([BitConverter]::ToString($sha256.ComputeHash($fingerprintBytes))).Replace('-', '')
|
|
} finally {
|
|
$sha256.Dispose()
|
|
}
|
|
$markerPath = Join-Path $Destination 'payload.json'
|
|
if (-not $Force -and (Test-Path -LiteralPath $markerPath)) {
|
|
try {
|
|
$marker = Get-Content -LiteralPath $markerPath -Raw | ConvertFrom-Json
|
|
if ($marker.nodeVersion -eq $NodeVersion -and $marker.sourceFingerprint -eq $fingerprint -and
|
|
(Test-Path -LiteralPath (Join-Path $Destination 'node.exe')) -and
|
|
(Test-Path -LiteralPath (Join-Path $Destination 'api\node_modules\express\package.json') -PathType Leaf) -and
|
|
(Test-Path -LiteralPath (Join-Path $Destination 'api\pnpm-lock.yaml')) -and
|
|
(Test-Path -LiteralPath (Join-Path $Destination 'api\LICENSE'))) {
|
|
return
|
|
}
|
|
} catch {
|
|
}
|
|
}
|
|
|
|
$CacheRoot = Join-Path $RepoRoot '.cache\node-runtime'
|
|
New-Item -ItemType Directory -Force -Path $CacheRoot | Out-Null
|
|
$archiveName = "node-v$NodeVersion-win-x64.zip"
|
|
$archivePath = Join-Path $CacheRoot $archiveName
|
|
$checksumsPath = Join-Path $CacheRoot "SHASUMS256-$NodeVersion.txt"
|
|
$distRoot = "https://nodejs.org/dist/v$NodeVersion"
|
|
if (-not (Test-Path -LiteralPath $archivePath)) {
|
|
Invoke-WebRequest -UseBasicParsing "$distRoot/$archiveName" -OutFile $archivePath
|
|
}
|
|
if (-not (Test-Path -LiteralPath $checksumsPath)) {
|
|
Invoke-WebRequest -UseBasicParsing "$distRoot/SHASUMS256.txt" -OutFile $checksumsPath
|
|
}
|
|
$checksumLine = Get-Content -LiteralPath $checksumsPath | Where-Object { $_ -match "\s$([regex]::Escape($archiveName))$" } | Select-Object -First 1
|
|
if (-not $checksumLine) { throw "Node checksum was not found for $archiveName" }
|
|
$expectedHash = ($checksumLine -split '\s+')[0].ToUpperInvariant()
|
|
$actualHash = (Get-Sha256Hex $archivePath).ToUpperInvariant()
|
|
if ($actualHash -ne $expectedHash) { throw "Node runtime checksum mismatch for $archiveName" }
|
|
|
|
$staging = Join-Path $BuildRoot ("music-api.staging." + $PID)
|
|
if (Test-Path -LiteralPath $staging) { Remove-Item -LiteralPath $staging -Recurse -Force }
|
|
New-Item -ItemType Directory -Force -Path $staging | Out-Null
|
|
$extractRoot = Join-Path $staging '_node'
|
|
Expand-Archive -LiteralPath $archivePath -DestinationPath $extractRoot -Force
|
|
$nodeRoot = Join-Path $extractRoot "node-v$NodeVersion-win-x64"
|
|
Copy-Item -LiteralPath (Join-Path $nodeRoot 'node.exe') -Destination (Join-Path $staging 'node.exe')
|
|
Copy-Item -LiteralPath (Join-Path $nodeRoot 'LICENSE') -Destination (Join-Path $staging 'NODE_LICENSE')
|
|
Copy-Item -LiteralPath (Join-Path $nodeRoot 'README.md') -Destination (Join-Path $staging 'NODE_README.md')
|
|
Remove-Item -LiteralPath $extractRoot -Recurse -Force
|
|
|
|
$apiDestination = Join-Path $staging 'api'
|
|
robocopy $ApiSource $apiDestination /E /XD node_modules .git /NFL /NDL /NJH /NJS /NP | Out-Null
|
|
if ($LASTEXITCODE -ge 8) { throw "Failed to copy music API source (robocopy exit $LASTEXITCODE)." }
|
|
Copy-Item -LiteralPath (Join-Path $SourceRoot 'MOEKOE_LICENSE') -Destination (Join-Path $staging 'MOEKOE_LICENSE')
|
|
Copy-Item -LiteralPath (Join-Path $SourceRoot 'MOEKOE_README.md') -Destination (Join-Path $staging 'MOEKOE_README.md')
|
|
Copy-Item -LiteralPath (Join-Path $SourceRoot 'UPSTREAM_COMMIT') -Destination (Join-Path $staging 'UPSTREAM_COMMIT')
|
|
Copy-Item -LiteralPath (Join-Path $SourceRoot 'API_SUBMODULE_COMMIT') -Destination (Join-Path $staging 'API_SUBMODULE_COMMIT')
|
|
Copy-Item -LiteralPath (Join-Path $SourceRoot 'YMhut_PATCHES.md') -Destination (Join-Path $staging 'YMhut_PATCHES.md')
|
|
|
|
Push-Location $apiDestination
|
|
try {
|
|
& corepack pnpm install --prod --frozen-lockfile --ignore-scripts --package-import-method=copy --config.node-linker=hoisted --store-dir (Join-Path $RepoRoot '.cache\pnpm-store-music')
|
|
if ($LASTEXITCODE -ne 0) { throw "pnpm failed to install the locked music API dependencies." }
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
|
|
[ordered]@{
|
|
protocolVersion = 1
|
|
moekoeCommit = '4581660209c9cdd07669e8bd5bcbe136b7d91e02'
|
|
apiCommit = '06560e3e053bda1ab830750db6f645bab703f824'
|
|
nodeVersion = $NodeVersion
|
|
sourceFingerprint = $fingerprint
|
|
} | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $staging 'payload.json') -Encoding utf8
|
|
|
|
if (Test-Path -LiteralPath $Destination) { Remove-Item -LiteralPath $Destination -Recurse -Force }
|
|
Move-Item -LiteralPath $staging -Destination $Destination
|