fix(jenkins): cache provision downloads by github digest

This commit is contained in:
2026-05-19 20:54:43 +08:00
parent 8919f37b2c
commit c84f7b5483
5 changed files with 145 additions and 49 deletions

View File

@@ -49,8 +49,7 @@ pipeline {
string(name: 'PROVISION_DOWNLOADS_DIR', defaultValue: 'provision-tool-downloads', description: 'Windows 下载阶段暂存 SpacetimeDB/otelcol 安装包的工作区相对目录')
string(name: 'PROVISION_TOOLS_DIR', defaultValue: 'provision-tools', description: '目标机工作区内由已下载安装包生成的工具包目录')
string(name: 'PROVISION_DOWNLOAD_PROXY', defaultValue: '', description: '可选Windows 下载 SpacetimeDB 和 otelcol-contrib 时使用的代理地址,例如 http://127.0.0.1:7890留空不设置代理')
string(name: 'SPACETIME_INSTALLER_URL', defaultValue: 'https://install.spacetimedb.com', description: 'Windows 下载 SpacetimeDB 官方安装脚本的地址;目标机不访问该地址')
string(name: 'SPACETIME_DOWNLOAD_ROOT', defaultValue: 'https://github.com/clockworklabs/SpacetimeDB/releases/latest/download', description: 'Windows 下载 SpacetimeDB Linux update installer 的根地址;目标机不访问该地址')
string(name: 'SPACETIME_DOWNLOAD_ROOT', defaultValue: 'https://github.com/clockworklabs/SpacetimeDB/releases/latest/download', description: 'Windows 下载 SpacetimeDB Linux release tarball 的根地址;目标机不访问该地址')
string(name: 'SPACETIME_TARGET_HOST', defaultValue: 'x86_64-unknown-linux-gnu', description: '目标机 SpacetimeDB 预编译包 host tripledevelopment/release Linux amd64 使用默认值')
string(name: 'SPACETIME_ROOT', defaultValue: '/stdb', description: 'SpacetimeDB root-dir')
string(name: 'RELEASE_ROOT', defaultValue: '/opt/genarrative/releases', description: 'release 根目录')
@@ -115,9 +114,6 @@ pipeline {
if (!(params.OTELCOL_VERSION?.trim() ==~ /^[0-9]+\.[0-9]+\.[0-9]+$/)) {
error("OTELCOL_VERSION 格式应为 x.y.z: ${params.OTELCOL_VERSION}")
}
if (!(params.SPACETIME_INSTALLER_URL?.trim() ==~ /^https?:\/\/\S+$/)) {
error("SPACETIME_INSTALLER_URL 只能填写 http:// 或 https:// 开头的地址: ${params.SPACETIME_INSTALLER_URL}")
}
if (!(params.SPACETIME_DOWNLOAD_ROOT?.trim() ==~ /^https?:\/\/\S+$/)) {
error('SPACETIME_DOWNLOAD_ROOT 不能为空。')
}
@@ -152,7 +148,6 @@ pipeline {
$otelVersion = if ($env:OTELCOL_VERSION) { $env:OTELCOL_VERSION } else { '0.151.0' }
$prepareOtel = if ($env:ENABLE_OTELCOL) { $env:ENABLE_OTELCOL } else { 'true' }
$otelRoot = if ($env:OTELCOL_DOWNLOAD_ROOT) { $env:OTELCOL_DOWNLOAD_ROOT.TrimEnd('/') } else { 'https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download' }
$spacetimeInstallerUrl = if ($env:SPACETIME_INSTALLER_URL) { $env:SPACETIME_INSTALLER_URL } else { 'https://install.spacetimedb.com' }
$spacetimeDownloadRoot = if ($env:SPACETIME_DOWNLOAD_ROOT) { $env:SPACETIME_DOWNLOAD_ROOT.TrimEnd('/') } else { 'https://github.com/clockworklabs/SpacetimeDB/releases/latest/download' }
$spacetimeTargetHost = if ($env:SPACETIME_TARGET_HOST) { $env:SPACETIME_TARGET_HOST } else { 'x86_64-unknown-linux-gnu' }
$downloadProxy = if ($env:PROVISION_DOWNLOAD_PROXY) { $env:PROVISION_DOWNLOAD_PROXY } else { '' }
@@ -165,11 +160,11 @@ pipeline {
Write-Host "[prepare-provision-downloads] download dir: ${downloadsDir}"
if (Test-Path -LiteralPath $downloadsDir) {
Write-Host "[prepare-provision-downloads] 清理旧下载目录: ${downloadsDir}"
Remove-Item -LiteralPath $downloadsDir -Recurse -Force
Write-Host "[prepare-provision-downloads] 复用已有下载目录: ${downloadsDir}"
} else {
New-Item -ItemType Directory -Force -Path $downloadsDir | Out-Null
Write-Host "[prepare-provision-downloads] 已创建下载目录: ${downloadsDir}"
}
New-Item -ItemType Directory -Force -Path $downloadsDir | Out-Null
Write-Host "[prepare-provision-downloads] 已创建下载目录: ${downloadsDir}"
if ($downloadProxy) {
$env:HTTP_PROXY = $downloadProxy
@@ -178,17 +173,94 @@ pipeline {
Write-Host "[prepare-provision-downloads] 已配置 Windows 下载代理: $($downloadProxy -replace '://.*', '://***')"
}
function Get-GithubReleaseAssetDigest {
param(
[Parameter(Mandatory=$true)][string]$Repository,
[Parameter(Mandatory=$true)][string]$ReleaseSelector,
[Parameter(Mandatory=$true)][string]$AssetName
)
$request = @{
Uri = "https://api.github.com/repos/${Repository}/${ReleaseSelector}"
Headers = @{
Accept = 'application/vnd.github+json'
'User-Agent' = 'Genarrative-Server-Provision'
}
ErrorAction = 'Stop'
}
if ($downloadProxy) {
$request.Proxy = $downloadProxy
}
Write-Host "[prepare-provision-downloads] 查询 GitHub digest: repo=${Repository} release=${ReleaseSelector} asset=${AssetName}"
$release = Invoke-RestMethod @request
$asset = $release.assets | Where-Object { $_.name -eq $AssetName } | Select-Object -First 1
if (-not $asset) {
throw "[prepare-provision-downloads] GitHub release 未找到资产: ${Repository}/${AssetName}"
}
if (-not $asset.digest) {
throw "[prepare-provision-downloads] GitHub release 未返回 digest: ${Repository}/${AssetName}"
}
Write-Host "[prepare-provision-downloads] GitHub digest ${AssetName}: $($asset.digest)"
return $asset.digest
}
function Get-FileDigest {
param(
[Parameter(Mandatory=$true)][string]$Path,
[Parameter(Mandatory=$true)][string]$Algorithm
)
$fileHash = Get-FileHash -Algorithm $Algorithm -LiteralPath $Path
return $fileHash.Hash.ToLowerInvariant()
}
function Test-DownloadDigestMatch {
param(
[Parameter(Mandatory=$true)][string]$Path,
[Parameter(Mandatory=$true)][string]$ExpectedDigest
)
$parts = $ExpectedDigest.Split(':', 2)
if ($parts.Length -ne 2) {
throw "[prepare-provision-downloads] 无法解析 GitHub digest: ${ExpectedDigest}"
}
$algorithm = $parts[0].Trim().ToLowerInvariant()
$expectedHash = $parts[1].Trim().ToLowerInvariant()
if ($algorithm -ne 'sha256') {
throw "[prepare-provision-downloads] 暂不支持的 GitHub digest 算法: ${algorithm}"
}
$localHash = Get-FileDigest -Path $Path -Algorithm 'SHA256'
return $localHash -eq $expectedHash
}
function Invoke-ProvisionDownload {
param(
[Parameter(Mandatory=$true)][string]$Label,
[Parameter(Mandatory=$true)][string]$Url,
[Parameter(Mandatory=$true)][string]$Output
[Parameter(Mandatory=$true)][string]$Output,
[string]$ExpectedDigest = ''
)
if ($ExpectedDigest) {
if (Test-Path -LiteralPath $Output) {
if (Test-DownloadDigestMatch -Path $Output -ExpectedDigest $ExpectedDigest) {
$existingItem = Get-Item -LiteralPath $Output
Write-Host "[prepare-provision-downloads] 已存在且校验一致,跳过下载: ${Label} bytes=$($existingItem.Length) path=${Output}"
return
}
Write-Host "[prepare-provision-downloads] 已存在但校验不一致,重新下载: ${Label} path=${Output}"
}
}
Write-Host "[prepare-provision-downloads] 下载 ${Label}: ${Url}"
$tempOutput = "${Output}.download"
if (Test-Path -LiteralPath $tempOutput) {
Remove-Item -LiteralPath $tempOutput -Force
}
$curl = Get-Command curl.exe -ErrorAction SilentlyContinue
if ($curl) {
$arguments = @('-fL', '--retry', '3', '--retry-delay', '2', '-o', $Output)
$arguments = @('-fL', '--retry', '3', '--retry-delay', '2', '-o', $tempOutput)
if ($downloadProxy) {
$arguments += @('--proxy', $downloadProxy)
}
@@ -196,46 +268,51 @@ pipeline {
& $curl.Source @arguments
$exitCode = $LASTEXITCODE
if ($exitCode -ne 0) {
throw "[prepare-provision-downloads] curl 下载失败: ${Label}, exit=${exitCode}"
throw "[prepare-provision-downloads] curl 下载失败: ${Label}, exit=${exitCode}"
}
} else {
$parameters = @{
Uri = $Url
OutFile = $tempOutput
UseBasicParsing = $true
}
if ($downloadProxy) {
$parameters.Proxy = $downloadProxy
}
Invoke-WebRequest @parameters
}
} else {
$parameters = @{
Uri = $Url
OutFile = $Output
UseBasicParsing = $true
}
if ($downloadProxy) {
$parameters.Proxy = $downloadProxy
}
Invoke-WebRequest @parameters
}
$item = Get-Item -LiteralPath $Output
$item = Get-Item -LiteralPath $tempOutput
if ($item.Length -le 0) {
throw "[prepare-provision-downloads] 下载结果为空: ${Output}"
throw "[prepare-provision-downloads] 下载结果为空: ${tempOutput}"
}
Write-Host "[prepare-provision-downloads] 已下载 ${Label}: bytes=$($item.Length) path=${Output}"
if ($ExpectedDigest) {
if (-not (Test-DownloadDigestMatch -Path $tempOutput -ExpectedDigest $ExpectedDigest)) {
throw "[prepare-provision-downloads] 下载结果校验失败: ${Label}"
}
}
Move-Item -LiteralPath $tempOutput -Destination $Output -Force
$finalItem = Get-Item -LiteralPath $Output
Write-Host "[prepare-provision-downloads] 已下载 ${Label}: bytes=$($finalItem.Length) path=${Output}"
}
$installerPath = Join-Path $downloadsDir 'spacetime-install.sh'
Invoke-ProvisionDownload -Label 'SpacetimeDB install script' -Url $spacetimeInstallerUrl -Output $installerPath
$spacetimeUpdateName = "spacetimedb-update-${spacetimeTargetHost}"
$spacetimeUpdateUrl = "${spacetimeDownloadRoot}/${spacetimeUpdateName}"
Invoke-ProvisionDownload -Label "SpacetimeDB Linux update installer ${spacetimeTargetHost}" -Url $spacetimeUpdateUrl -Output (Join-Path $downloadsDir $spacetimeUpdateName)
$spacetimeArchiveName = "spacetime-${spacetimeTargetHost}.tar.gz"
$spacetimeArchiveUrl = "${spacetimeDownloadRoot}/${spacetimeArchiveName}"
$spacetimeArchiveDigest = Get-GithubReleaseAssetDigest -Repository 'clockworklabs/SpacetimeDB' -ReleaseSelector 'releases/latest' -AssetName $spacetimeArchiveName
Invoke-ProvisionDownload -Label "SpacetimeDB release tarball ${spacetimeTargetHost}" -Url $spacetimeArchiveUrl -Output (Join-Path $downloadsDir $spacetimeArchiveName) -ExpectedDigest $spacetimeArchiveDigest
if ($prepareOtel -eq 'true') {
$otelArchiveName = "otelcol-contrib_${otelVersion}_linux_amd64.tar.gz"
$otelUrl = "${otelRoot}/v${otelVersion}/${otelArchiveName}"
Invoke-ProvisionDownload -Label "otelcol-contrib ${otelVersion} linux amd64" -Url $otelUrl -Output (Join-Path $downloadsDir $otelArchiveName)
$otelDigest = Get-GithubReleaseAssetDigest -Repository 'open-telemetry/opentelemetry-collector-releases' -ReleaseSelector "releases/tags/v${otelVersion}" -AssetName $otelArchiveName
Invoke-ProvisionDownload -Label "otelcol-contrib ${otelVersion} linux amd64" -Url $otelUrl -Output (Join-Path $downloadsDir $otelArchiveName) -ExpectedDigest $otelDigest
} else {
Write-Host "[prepare-provision-downloads] ENABLE_OTELCOL=${prepareOtel},跳过 otelcol-contrib 下载。"
}
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
$manifest = @(
"spacetime installer ${spacetimeInstallerUrl}",
"spacetime update ${spacetimeDownloadRoot}/${spacetimeUpdateName}",
"spacetime release tarball ${spacetimeArchiveUrl}",
"spacetime target host ${spacetimeTargetHost}",
"otelcol-contrib ${otelVersion} prepare=${prepareOtel}"
)
@@ -311,7 +388,6 @@ BASH
OTELCOL_VERSION="${OTELCOL_VERSION:-0.151.0}" \
PREPARE_OTELCOL="${ENABLE_OTELCOL:-true}" \
PROVISION_REQUIRE_LOCAL_DOWNLOADS="true" \
SPACETIME_INSTALLER_URL="${SPACETIME_INSTALLER_URL:-https://install.spacetimedb.com}" \
SPACETIME_DOWNLOAD_ROOT="${SPACETIME_DOWNLOAD_ROOT:-https://github.com/clockworklabs/SpacetimeDB/releases/latest/download}" \
SPACETIME_TARGET_HOST="${SPACETIME_TARGET_HOST:-x86_64-unknown-linux-gnu}" \
scripts/prepare-server-provision-tools.sh