Files
dingMCP/distribution/bootstrap-windows.template.ps1
T

158 lines
8.3 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
$dingUtf8 = New-Object System.Text.UTF8Encoding($false)
[Console]::InputEncoding = $dingUtf8
[Console]::OutputEncoding = $dingUtf8
$OutputEncoding = $dingUtf8
if (Get-Variable -Name PSNativeCommandUseErrorActionPreference -ErrorAction SilentlyContinue) {
$PSNativeCommandUseErrorActionPreference = $false
}
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
$dingSourceBases = @({{NODE_SOURCE_BASES_POWERSHELL}})
$dingInstallerUrls = @({{INSTALLER_URLS_POWERSHELL}})
$dingInstallerSha256 = "{{INSTALLER_SHA256}}"
$dingPublicKeyUrls = @({{PUBLIC_KEY_URLS_POWERSHELL}})
$dingPublicKeySha256 = "{{PUBLIC_KEY_SHA256}}"
$dingManifestUrl = "{{MANIFEST_URL}}"
$dingManifestMirrorUrls = @({{MANIFEST_MIRROR_URLS_POWERSHELL}})
function Write-DingProgress([string] $dingMessage) {
[Console]::Error.WriteLine("[dingtalk-approval] $dingMessage")
}
function Get-DingSha256([string] $dingPath) {
return (Get-FileHash -Algorithm SHA256 -LiteralPath $dingPath).Hash.ToLowerInvariant()
}
function Save-DingDownload([string[]] $dingUrls, [string] $dingTarget, [string] $dingExpectedSha256) {
$dingLastError = $null
$dingCurl = Get-Command curl.exe -ErrorAction SilentlyContinue
foreach ($dingUrl in $dingUrls) {
Write-DingProgress "正在下载并校验:$dingUrl"
try {
if (Test-Path -LiteralPath $dingTarget) { Remove-Item -LiteralPath $dingTarget -Force }
if ($dingCurl) {
$dingCurlHttp = & $dingCurl.Source --fail --location --silent --show-error --connect-timeout 10 --max-time 90 --speed-time 20 --speed-limit 1024 --write-out "%{http_code}" --output $dingTarget $dingUrl
$dingCurlExit = $LASTEXITCODE
if ($dingCurlExit -ne 0) {
if ($dingCurlHttp -match '^[1-5][0-9][0-9]$' -and $dingCurlHttp -ne '000') { throw "HTTP $dingCurlHttp" }
throw "curl.exe 退出码 $dingCurlExit"
}
} else {
Invoke-WebRequest -UseBasicParsing -Uri $dingUrl -OutFile $dingTarget -TimeoutSec 90
}
} catch {
$dingLastError = $_.Exception.Message
$dingStatusCode = $null
if ($_.Exception.Response -and $_.Exception.Response.StatusCode) {
try { $dingStatusCode = [int]$_.Exception.Response.StatusCode } catch { $dingStatusCode = $null }
}
if ($null -eq $dingStatusCode -and $dingLastError -match 'HTTP ([0-9][0-9][0-9])') {
$dingStatusCode = [int]$Matches[1]
}
if ($null -eq $dingStatusCode -or $dingStatusCode -eq 404) { continue }
throw "下载失败且不允许换源(HTTP $dingStatusCode):$dingUrl;$dingLastError"
}
$dingActualSha256 = Get-DingSha256 $dingTarget
if ($dingActualSha256 -ne $dingExpectedSha256) {
throw "下载文件 SHA-256 不匹配,停止安装:$dingUrl"
}
return
}
throw "所有下载源均不可用:$dingLastError"
}
function Test-DingNode([string] $dingNodePath, [bool] $dingRequireAdjacentNpm) {
if (-not $dingNodePath -or -not (Test-Path -LiteralPath $dingNodePath -PathType Leaf)) { return $false }
try {
& $dingNodePath -e "const [a,b]=process.versions.node.split('.').map(Number);process.exit(a>22||(a===22&&b>=13)?0:1)"
if ($LASTEXITCODE -ne 0) { return $false }
if ($dingRequireAdjacentNpm) {
$dingNpmCli = Join-Path (Split-Path -Parent $dingNodePath) "node_modules\npm\bin\npm-cli.js"
return Test-Path -LiteralPath $dingNpmCli -PathType Leaf
}
return $null -ne (Get-Command npm -ErrorAction SilentlyContinue)
} catch { return $false }
}
$dingArchitecture = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant()
switch ($dingArchitecture) {
"x64" { $dingNodeFile = "{{NODE_WIN32_X64_FILE}}"; $dingNodeSha256 = "{{NODE_WIN32_X64_SHA256}}" }
"arm64" { $dingNodeFile = "{{NODE_WIN32_ARM64_FILE}}"; $dingNodeSha256 = "{{NODE_WIN32_ARM64_SHA256}}" }
default { throw "不支持的 Windows CPU 架构:$dingArchitecture" }
}
Write-DingProgress "1/4 检查 Windows-$dingArchitecture 的 Node/npm 运行环境"
$dingSystemNode = Get-Command node -ErrorAction SilentlyContinue
$dingNodePath = if ($dingSystemNode) { $dingSystemNode.Source } else { $null }
if (-not (Test-DingNode $dingNodePath $false)) {
if (-not $env:LOCALAPPDATA -and -not $env:DINGTALK_APPROVAL_CLIENT_HOME) {
throw "无法确定当前用户数据目录:LOCALAPPDATA 为空"
}
$dingClientRoot = if ($env:DINGTALK_APPROVAL_CLIENT_HOME) {
[IO.Path]::GetFullPath($env:DINGTALK_APPROVAL_CLIENT_HOME)
} else {
Join-Path $env:LOCALAPPDATA "DingTalkApproval"
}
$dingRuntimeParent = Join-Path $dingClientRoot "node"
$dingRuntimeName = [IO.Path]::GetFileNameWithoutExtension($dingNodeFile)
$dingRuntimeDir = Join-Path $dingRuntimeParent $dingRuntimeName
$dingNodePath = Join-Path $dingRuntimeDir "node.exe"
if (-not (Test-DingNode $dingNodePath $true)) {
Write-DingProgress "2/4 系统 Node 不可用,准备插件私有 Node;下载可能需要一些时间"
New-Item -ItemType Directory -Path $dingRuntimeParent -Force | Out-Null
$dingRuntimeTemp = Join-Path ([IO.Path]::GetTempPath()) ("dingmcp-node-" + [guid]::NewGuid().ToString("N"))
New-Item -ItemType Directory -Path $dingRuntimeTemp -Force | Out-Null
try {
$dingRuntimeArchive = Join-Path $dingRuntimeTemp $dingNodeFile
$dingRuntimeUrls = @($dingSourceBases | ForEach-Object { $_ + $dingNodeFile })
Save-DingDownload $dingRuntimeUrls $dingRuntimeArchive $dingNodeSha256
$dingExtractRoot = Join-Path $dingRuntimeTemp "extract"
Expand-Archive -LiteralPath $dingRuntimeArchive -DestinationPath $dingExtractRoot -Force
$dingExtractedDir = Join-Path $dingExtractRoot $dingRuntimeName
$dingExtractedNode = Join-Path $dingExtractedDir "node.exe"
if (-not (Test-DingNode $dingExtractedNode $true)) { throw "便携 Node 归档缺少可用的 node.exe 或 npm-cli.js" }
if (Test-Path -LiteralPath $dingRuntimeDir) { Remove-Item -LiteralPath $dingRuntimeDir -Recurse -Force }
Move-Item -LiteralPath $dingExtractedDir -Destination $dingRuntimeDir
} finally {
if (Test-Path -LiteralPath $dingRuntimeTemp) { Remove-Item -LiteralPath $dingRuntimeTemp -Recurse -Force }
}
} else {
Write-DingProgress "2/4 复用已安装的插件私有 Node"
}
} else {
Write-DingProgress "2/4 复用兼容的系统 Node"
}
Write-DingProgress "3/4 下载并校验签名安装器与发布公钥"
$dingInstallTemp = Join-Path ([IO.Path]::GetTempPath()) ("dingmcp-install-" + [guid]::NewGuid().ToString("N"))
New-Item -ItemType Directory -Path $dingInstallTemp -Force | Out-Null
try {
$dingInstaller = Join-Path $dingInstallTemp "installer.mjs"
$dingPublicKey = Join-Path $dingInstallTemp "release-signing-public.pem"
Save-DingDownload $dingInstallerUrls $dingInstaller $dingInstallerSha256
Save-DingDownload $dingPublicKeyUrls $dingPublicKey $dingPublicKeySha256
Write-DingProgress "4/4 执行安装器;最终 JSON 输出到 stdout,阶段进度输出到 stderr"
$dingInstallerArgs = @($dingInstaller, "install", "--manifest-url", $dingManifestUrl)
foreach ($dingManifestMirrorUrl in $dingManifestMirrorUrls) {
$dingInstallerArgs += @("--manifest-mirror-url", $dingManifestMirrorUrl)
}
$dingInstallerArgs += @("--public-key", $dingPublicKey, "--json")
$dingNodeProxyArgs = @()
$dingProxyConfigured = $env:HTTPS_PROXY -or $env:HTTP_PROXY -or $env:ALL_PROXY
$dingProxyAlreadyEnabled = ($env:NODE_OPTIONS -match '(^|\s)--use-env-proxy($|\s)') -or ($env:NODE_USE_ENV_PROXY -match '^(1|true)$')
if ($dingProxyConfigured -and -not $dingProxyAlreadyEnabled) {
& $dingNodePath -e "process.exit(process.allowedNodeEnvironmentFlags.has('--use-env-proxy') ? 0 : 1)"
if ($LASTEXITCODE -eq 0) { $dingNodeProxyArgs = @("--use-env-proxy") }
}
$dingInstallOutput = & $dingNodePath @dingNodeProxyArgs @dingInstallerArgs
$dingInstallExit = $LASTEXITCODE
if ($dingInstallExit -ne 0) { throw "钉钉审批安装器执行失败:$dingInstallOutput" }
$dingResult = $dingInstallOutput | ConvertFrom-Json
if ($dingResult.ok -ne $true) { throw "钉钉审批安装失败:$($dingResult.error)" }
$dingResult | ConvertTo-Json -Depth 20 -Compress
} finally {
if (Test-Path -LiteralPath $dingInstallTemp) { Remove-Item -LiteralPath $dingInstallTemp -Recurse -Force }
}