CI / Client installer (macos-latest) (push) Canceled after 0s
CI / Client installer (ubuntu-latest) (push) Canceled after 0s
CI / Client installer (windows-latest) (push) Canceled after 0s
CI / test (22) (push) Canceled after 0s
CI / test (24) (push) Canceled after 0s
CI / release-smoke (push) Canceled after 0s
128 lines
6.6 KiB
PowerShell
128 lines
6.6 KiB
PowerShell
$ErrorActionPreference = "Stop"
|
||
$ProgressPreference = "SilentlyContinue"
|
||
if (Get-Variable -Name PSNativeCommandUseErrorActionPreference -ErrorAction SilentlyContinue) {
|
||
$PSNativeCommandUseErrorActionPreference = $false
|
||
}
|
||
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
|
||
|
||
$dingSourceBases = @({{NODE_SOURCE_BASES_POWERSHELL}})
|
||
$dingInstallerUrl = "{{INSTALLER_URL}}"
|
||
$dingInstallerSha256 = "{{INSTALLER_SHA256}}"
|
||
$dingPublicKeyUrl = "{{PUBLIC_KEY_URL}}"
|
||
$dingPublicKeySha256 = "{{PUBLIC_KEY_SHA256}}"
|
||
$dingManifestUrl = "{{MANIFEST_URL}}"
|
||
|
||
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
|
||
foreach ($dingUrl in $dingUrls) {
|
||
Write-DingProgress "正在下载并校验:$dingUrl"
|
||
try {
|
||
if (Test-Path -LiteralPath $dingTarget) { Remove-Item -LiteralPath $dingTarget -Force }
|
||
Invoke-WebRequest -UseBasicParsing -Uri $dingUrl -OutFile $dingTarget -TimeoutSec 300
|
||
} 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 -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([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 @($dingInstallerUrl) $dingInstaller $dingInstallerSha256
|
||
Save-DingDownload @($dingPublicKeyUrl) $dingPublicKey $dingPublicKeySha256
|
||
Write-DingProgress "4/4 执行安装器;最终 JSON 输出到 stdout,阶段进度输出到 stderr"
|
||
$dingInstallOutput = & $dingNodePath $dingInstaller install --manifest-url $dingManifestUrl --public-key $dingPublicKey --json
|
||
$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 }
|
||
}
|