mirror of
https://github.com/Mesh-LLM/mesh-llm.git
synced 2026-08-08 22:23:19 -04:00
* ci: compose reusable products and add Depot routing * ci: configure job-local sccache storage * fix: harden Windows artifact composition * test: assert pinned nightly artifact action * ci: allow superseded SDK smokes to cancel * docs: document cancellable CI fan-in gates * ci: harden composable build graph and metrics * fix(ci): install actionlint from verified release * fix(installer): normalize runtime digest paths * fix(ci): align installer contract output * docs(ci): ground runner image migration plan * ci: route trusted ARM lanes through Depot selector * ci: enable remote sccache for fast lanes * fix(ci): await remote sccache writes * fix(ci): align sccache policy contract * refactor(ci): reuse typed SDK and static ABI inputs * fix(ci): reuse configured sccache server * fix(ci): harden exact native cache reuse * fix(ci): make PR compiler cache read-only * fix(ci): isolate pull request compiler writes * feat(ci): produce immutable Node addon artifacts * fix(ci): restrict Depot canary to main * fix(ci): isolate Depot canary cache keys
561 lines
19 KiB
PowerShell
Executable file
561 lines
19 KiB
PowerShell
Executable file
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Version,
|
|
[string]$OutputDir = "dist",
|
|
[string]$Flavor = ""
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $scriptDir ".."))
|
|
$releaseBinDir = if ($env:MESH_LLM_RELEASE_BIN_DIR) {
|
|
$env:MESH_LLM_RELEASE_BIN_DIR
|
|
} else {
|
|
Join-Path $repoRoot "target\release"
|
|
}
|
|
$nativeRuntimeRoot = if ($env:MESH_LLM_NATIVE_RUNTIME_ROOT) {
|
|
$env:MESH_LLM_NATIVE_RUNTIME_ROOT
|
|
} else {
|
|
Join-Path $repoRoot "dist\native-runtimes"
|
|
}
|
|
$attestationSigningKeyFile = $env:MESH_RELEASE_ATTESTATION_SIGNING_KEY_FILE
|
|
$attestationPublicKeyFile = $env:MESH_RELEASE_ATTESTATION_PUBLIC_KEY_FILE
|
|
$attestationVerifier = $env:MESH_RELEASE_ATTESTATION_VERIFIER
|
|
$precomposedProductDir = $env:MESH_LLM_PRECOMPOSED_PRODUCT_DIR
|
|
$attestationPreverified = if ([string]::IsNullOrWhiteSpace($env:MESH_RELEASE_ATTESTATION_PREVERIFIED)) {
|
|
"0"
|
|
} else {
|
|
$env:MESH_RELEASE_ATTESTATION_PREVERIFIED.Trim()
|
|
}
|
|
|
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
|
|
|
function Normalize-RecipeArgument {
|
|
param(
|
|
[AllowEmptyString()]
|
|
[string]$Value,
|
|
[string[]]$KnownNames = @()
|
|
)
|
|
|
|
if ($null -eq $Value) {
|
|
return $Value
|
|
}
|
|
|
|
$normalized = $Value.Trim()
|
|
if (-not $normalized) {
|
|
return ""
|
|
}
|
|
|
|
if ($normalized -match '^(?<name>[A-Za-z_][A-Za-z0-9_-]*)=(?<value>.*)$') {
|
|
$matchedName = $Matches.name
|
|
$isKnownName = $KnownNames.Count -eq 0
|
|
foreach ($knownName in $KnownNames) {
|
|
if ($matchedName.Equals($knownName, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
$isKnownName = $true
|
|
break
|
|
}
|
|
}
|
|
|
|
if ($isKnownName) {
|
|
$normalized = $Matches.value
|
|
}
|
|
}
|
|
|
|
if ($normalized.Length -ge 2) {
|
|
$first = $normalized[0]
|
|
$last = $normalized[$normalized.Length - 1]
|
|
if (($first -eq '"' -and $last -eq '"') -or ($first -eq "'" -and $last -eq "'")) {
|
|
$normalized = $normalized.Substring(1, $normalized.Length - 2)
|
|
}
|
|
}
|
|
|
|
return $normalized.Trim()
|
|
}
|
|
|
|
function Get-ReleaseFlavor {
|
|
param([string]$RequestedFlavor)
|
|
|
|
if ($RequestedFlavor) {
|
|
switch ($RequestedFlavor.ToLowerInvariant()) {
|
|
"hip" { return "rocm" }
|
|
default { return $RequestedFlavor.ToLowerInvariant() }
|
|
}
|
|
}
|
|
|
|
return "cpu"
|
|
}
|
|
|
|
function Get-BinaryFlavor {
|
|
param([string]$RequestedFlavor)
|
|
|
|
# The "release flavor" (outer archive name) and the "binary flavor"
|
|
# (inner executable suffix / runtime BinaryFlavor lookup) are not
|
|
# always the same. hip archives contain -rocm binaries.
|
|
if ($RequestedFlavor) {
|
|
switch ($RequestedFlavor.ToLowerInvariant()) {
|
|
"hip" { return "rocm" }
|
|
default { return $RequestedFlavor.ToLowerInvariant() }
|
|
}
|
|
}
|
|
|
|
return "cpu"
|
|
}
|
|
|
|
function Get-FlavorSuffix {
|
|
param([string]$BinaryFlavor)
|
|
|
|
if (-not $BinaryFlavor -or $BinaryFlavor -in @("cpu", "metal")) {
|
|
return ""
|
|
}
|
|
|
|
return "-$BinaryFlavor"
|
|
}
|
|
|
|
function New-ReleaseAssetName {
|
|
param(
|
|
[string]$Prefix,
|
|
[string]$TargetTriple,
|
|
[string]$ArchiveExt,
|
|
[string]$BinaryFlavor
|
|
)
|
|
|
|
return "$Prefix-$TargetTriple$(Get-FlavorSuffix $BinaryFlavor).$ArchiveExt"
|
|
}
|
|
|
|
function Get-BundleBinaryName {
|
|
param(
|
|
[string]$BaseName,
|
|
[string]$BinaryFlavor
|
|
)
|
|
|
|
if ($BaseName -eq "mesh-llm") {
|
|
return "$BaseName.exe"
|
|
}
|
|
|
|
if ($BinaryFlavor) {
|
|
return "$BaseName-$BinaryFlavor.exe"
|
|
}
|
|
|
|
return "$BaseName.exe"
|
|
}
|
|
|
|
function New-ZipArchive {
|
|
param(
|
|
[string]$SourceDir,
|
|
[string]$ArchivePath
|
|
)
|
|
|
|
if (Test-Path $ArchivePath) {
|
|
Remove-Item $ArchivePath -Force
|
|
}
|
|
|
|
$parent = Split-Path -Parent $ArchivePath
|
|
if ($parent) {
|
|
New-Item -ItemType Directory -Path $parent -Force | Out-Null
|
|
}
|
|
|
|
[System.IO.Compression.ZipFile]::CreateFromDirectory(
|
|
$SourceDir,
|
|
$ArchivePath,
|
|
[System.IO.Compression.CompressionLevel]::Optimal,
|
|
$true
|
|
)
|
|
}
|
|
|
|
function Get-Sha256Hex {
|
|
param([string]$Path)
|
|
|
|
# Use the .NET SHA-256 API directly instead of Get-FileHash. Under
|
|
# `powershell -NoProfile` on the CI runners, Microsoft.PowerShell.Utility
|
|
# module autoloading does not always resolve Get-FileHash, which caused
|
|
# release bundling to fail with CommandNotFoundException. The .NET API is
|
|
# always available regardless of module autoloading.
|
|
$sha256 = [System.Security.Cryptography.SHA256]::Create()
|
|
try {
|
|
$stream = [System.IO.File]::OpenRead($Path)
|
|
try {
|
|
$bytes = $sha256.ComputeHash($stream)
|
|
} finally {
|
|
$stream.Dispose()
|
|
}
|
|
} finally {
|
|
$sha256.Dispose()
|
|
}
|
|
return [System.BitConverter]::ToString($bytes).Replace("-", "").ToLowerInvariant()
|
|
}
|
|
|
|
function New-ChecksumSidecar {
|
|
param([string]$Path)
|
|
|
|
$hash = Get-Sha256Hex $Path
|
|
$name = Split-Path -Leaf $Path
|
|
Set-Content -Path "$Path.sha256" -Value "$hash $name" -NoNewline
|
|
}
|
|
|
|
function Require-File {
|
|
param([string]$Path)
|
|
|
|
if (-not (Test-Path $Path)) {
|
|
throw "Required file not found: $Path"
|
|
}
|
|
}
|
|
|
|
function Assert-FileChecksum {
|
|
param(
|
|
[string]$Path,
|
|
[string]$ChecksumPath
|
|
)
|
|
|
|
Require-File $Path
|
|
Require-File $ChecksumPath
|
|
$checksumText = (Get-Content -Path $ChecksumPath -Raw).Trim()
|
|
$expectedHash = ($checksumText -split '\s+')[0].ToLowerInvariant()
|
|
if ($expectedHash -notmatch '^[0-9a-f]{64}$') {
|
|
throw "Invalid SHA-256 checksum in ${ChecksumPath}"
|
|
}
|
|
|
|
$actualHash = Get-Sha256Hex $Path
|
|
if ($actualHash -ne $expectedHash) {
|
|
throw "SHA-256 checksum mismatch for ${Path}"
|
|
}
|
|
}
|
|
|
|
function Get-PythonCommand {
|
|
foreach ($name in @("python3", "python")) {
|
|
$command = Get-Command $name -ErrorAction SilentlyContinue
|
|
if ($command) {
|
|
return $command.Source
|
|
}
|
|
}
|
|
throw "python3 or python is required for release packaging"
|
|
}
|
|
|
|
function Assert-MeshBinaryVersion {
|
|
param(
|
|
[string]$Path,
|
|
[string]$ExpectedVersion
|
|
)
|
|
|
|
$expected = $ExpectedVersion.TrimStart("v")
|
|
$output = & $Path --version
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Release binary failed --version with exit code ${LASTEXITCODE}: $Path"
|
|
}
|
|
|
|
$parts = "$output".Trim() -split '\s+'
|
|
$actual = if ($parts.Count -gt 0) { $parts[$parts.Count - 1] } else { "" }
|
|
if ($actual -ne $expected) {
|
|
throw "Release binary version mismatch: expected $expected, got ${actual}. Binary: $Path. Output: $output"
|
|
}
|
|
}
|
|
|
|
function Test-HasValue {
|
|
param([string]$Value)
|
|
|
|
return -not [string]::IsNullOrWhiteSpace($Value)
|
|
}
|
|
|
|
function Resolve-RepositoryPath {
|
|
param([string]$Path)
|
|
|
|
# Bash composite actions expose Git-for-Windows paths such as
|
|
# /d/a/mesh-llm/mesh-llm/product-input through GITHUB_OUTPUT. Convert that
|
|
# form before handing it to the Windows .NET path APIs.
|
|
if ($Path -match '^/(?<drive>[A-Za-z])(?:/(?<tail>.*))?$') {
|
|
$drive = $Matches.drive.ToUpperInvariant()
|
|
$tail = "$($Matches.tail)".Replace("/", "\")
|
|
return [System.IO.Path]::GetFullPath("${drive}:\${tail}")
|
|
}
|
|
if ([System.IO.Path]::IsPathRooted($Path)) {
|
|
return [System.IO.Path]::GetFullPath($Path)
|
|
}
|
|
return [System.IO.Path]::GetFullPath((Join-Path $repoRoot $Path))
|
|
}
|
|
|
|
function Assert-AttestationConfig {
|
|
if ($attestationPreverified -eq "1") {
|
|
if ($env:MESH_RELEASE_HOST_PRESTAMPED -ne "1" -or -not (Test-HasValue $precomposedProductDir)) {
|
|
throw "MESH_RELEASE_ATTESTATION_PREVERIFIED=1 requires a pre-stamped precomposed product"
|
|
}
|
|
} elseif ($attestationPreverified -ne "0") {
|
|
throw "MESH_RELEASE_ATTESTATION_PREVERIFIED must be 0 or 1"
|
|
}
|
|
|
|
if ($env:MESH_RELEASE_HOST_PRESTAMPED -eq "1") {
|
|
if (-not (Test-HasValue $attestationPublicKeyFile)) {
|
|
throw "MESH_RELEASE_HOST_PRESTAMPED=1 requires MESH_RELEASE_ATTESTATION_PUBLIC_KEY_FILE"
|
|
}
|
|
return
|
|
}
|
|
if ((Test-HasValue $attestationSigningKeyFile) -and -not (Test-HasValue $attestationPublicKeyFile)) {
|
|
throw "MESH_RELEASE_ATTESTATION_PUBLIC_KEY_FILE is required when MESH_RELEASE_ATTESTATION_SIGNING_KEY_FILE is set"
|
|
}
|
|
|
|
if (-not (Test-HasValue $attestationSigningKeyFile) -and (Test-HasValue $attestationPublicKeyFile)) {
|
|
throw "MESH_RELEASE_ATTESTATION_SIGNING_KEY_FILE is required when MESH_RELEASE_ATTESTATION_PUBLIC_KEY_FILE is set"
|
|
}
|
|
}
|
|
|
|
function Invoke-ReleaseAttestationStamp {
|
|
param([string]$BinaryPath)
|
|
|
|
$inspectJson = $null
|
|
|
|
if ($env:MESH_RELEASE_HOST_PRESTAMPED -eq "1") {
|
|
if (-not (Test-Path $attestationPublicKeyFile) -or (Get-Item $attestationPublicKeyFile).Length -eq 0) {
|
|
throw "MESH_RELEASE_HOST_PRESTAMPED=1 requires a non-empty MESH_RELEASE_ATTESTATION_PUBLIC_KEY_FILE"
|
|
}
|
|
if ($attestationPreverified -eq "1") {
|
|
Write-Host "Release attestation: verified by immutable product composer"
|
|
return
|
|
}
|
|
if (Test-HasValue $attestationVerifier) {
|
|
Assert-FileChecksum -Path $attestationVerifier -ChecksumPath "${attestationVerifier}.sha256"
|
|
$inspectJson = & $attestationVerifier release-attestation inspect `
|
|
--binary $BinaryPath `
|
|
--public-key-file $attestationPublicKeyFile `
|
|
--json
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "prebuilt release-attestation verifier failed for pre-stamped $BinaryPath"
|
|
}
|
|
} else {
|
|
Push-Location $repoRoot
|
|
try {
|
|
$inspectJson = & cargo run -q -p xtask -- release-attestation inspect `
|
|
--binary $BinaryPath `
|
|
--public-key-file $attestationPublicKeyFile `
|
|
--json
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "release-attestation inspect failed for pre-stamped $BinaryPath"
|
|
}
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
$inspectStatus = ($inspectJson | ConvertFrom-Json).status
|
|
if ($inspectStatus -ne "valid") {
|
|
throw "pre-stamped release host attestation reported status '$inspectStatus' for $BinaryPath"
|
|
}
|
|
Write-Host "Release attestation: verified pre-stamped host"
|
|
return
|
|
}
|
|
|
|
if (-not (Test-HasValue $attestationSigningKeyFile)) {
|
|
Write-Host "Release attestation: missing (packaged binary left unstamped)"
|
|
return
|
|
}
|
|
|
|
if (-not (Test-Path $attestationSigningKeyFile) -or (Get-Item $attestationSigningKeyFile).Length -eq 0) {
|
|
Write-Host "Release attestation: signing key file is empty or missing ($attestationSigningKeyFile); leaving binary unstamped"
|
|
return
|
|
}
|
|
|
|
if (-not (Test-Path $attestationPublicKeyFile) -or (Get-Item $attestationPublicKeyFile).Length -eq 0) {
|
|
Write-Host "Release attestation: public key file is empty or missing ($attestationPublicKeyFile); leaving binary unstamped"
|
|
return
|
|
}
|
|
|
|
Push-Location $repoRoot
|
|
try {
|
|
& cargo run -q -p xtask -- release-attestation stamp `
|
|
--binary $BinaryPath `
|
|
--signing-key-file $attestationSigningKeyFile | Out-Null
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "release-attestation stamp failed for $BinaryPath"
|
|
}
|
|
|
|
$inspectJson = & cargo run -q -p xtask -- release-attestation inspect `
|
|
--binary $BinaryPath `
|
|
--public-key-file $attestationPublicKeyFile `
|
|
--json
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "release-attestation inspect failed for $BinaryPath"
|
|
}
|
|
Write-Host $inspectJson
|
|
$inspectStatus = ($inspectJson | ConvertFrom-Json).status
|
|
if ($inspectStatus -ne "valid") {
|
|
throw "release-attestation inspect reported status '$inspectStatus' for $BinaryPath"
|
|
}
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
function Copy-AndVerifyPrecomposedProduct {
|
|
param(
|
|
[string]$SourceDir,
|
|
[string]$BundleDir,
|
|
[string]$ExpectedVersion,
|
|
[string]$ExpectedBackend,
|
|
[string]$VerificationReport
|
|
)
|
|
|
|
$resolvedSourceDir = Resolve-RepositoryPath $SourceDir
|
|
if (-not (Test-Path -LiteralPath $resolvedSourceDir -PathType Container)) {
|
|
throw "Precomposed product directory does not exist: $resolvedSourceDir"
|
|
}
|
|
|
|
Require-File (Join-Path $resolvedSourceDir "product-manifest.json")
|
|
Require-File (Join-Path $resolvedSourceDir "host-imports.json")
|
|
foreach ($entry in [System.IO.Directory]::GetFileSystemEntries($resolvedSourceDir)) {
|
|
Copy-Item -LiteralPath $entry -Destination $BundleDir -Recurse -Force
|
|
}
|
|
|
|
$bundleBinary = Join-Path $BundleDir "mesh-llm.exe"
|
|
Require-File $bundleBinary
|
|
Assert-MeshBinaryVersion -Path $bundleBinary -ExpectedVersion $ExpectedVersion
|
|
Invoke-ReleaseAttestationStamp -BinaryPath $bundleBinary
|
|
|
|
$python = Get-PythonCommand
|
|
& $python (Join-Path $scriptDir "verify-host-dependencies.py") `
|
|
$bundleBinary `
|
|
--report $VerificationReport
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "backend-neutral host dependency verification failed"
|
|
}
|
|
|
|
$runtimeRoot = Join-Path $BundleDir "native-runtimes"
|
|
if (-not (Test-Path -LiteralPath $runtimeRoot -PathType Container)) {
|
|
throw "Precomposed product is missing native-runtimes: $runtimeRoot"
|
|
}
|
|
$runtimeDirs = @(Get-ChildItem -LiteralPath $runtimeRoot -Directory)
|
|
if ($runtimeDirs.Count -ne 1) {
|
|
throw "Precomposed product must contain exactly one native runtime; found $($runtimeDirs.Count)"
|
|
}
|
|
$runtimeDir = $runtimeDirs[0].FullName
|
|
Require-File (Join-Path $runtimeDir "manifest.json")
|
|
|
|
& bash (Join-Path $scriptDir "verify-native-runtime-package.sh") $runtimeDir
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "native runtime verification failed"
|
|
}
|
|
|
|
& $python (Join-Path $scriptDir "compose-product-bundle.py") `
|
|
--bundle $BundleDir `
|
|
--host $bundleBinary `
|
|
--runtime $runtimeDir `
|
|
--version $ExpectedVersion `
|
|
--backend $ExpectedBackend `
|
|
--check
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "precomposed product manifest does not match the packaged bytes"
|
|
}
|
|
}
|
|
|
|
$Version = Normalize-RecipeArgument $Version @("version")
|
|
$OutputDir = Normalize-RecipeArgument $OutputDir @("output", "output_dir", "outputdir")
|
|
$Flavor = Normalize-RecipeArgument $Flavor @("flavor", "backend")
|
|
|
|
Assert-AttestationConfig
|
|
|
|
$releaseFlavor = Get-ReleaseFlavor $Flavor
|
|
$binaryFlavor = Get-BinaryFlavor $Flavor
|
|
$targetTriple = "x86_64-pc-windows-msvc"
|
|
$archiveExt = "zip"
|
|
# Outer archive names use the release flavor; inner
|
|
# binary names use the binary flavor so the runtime finds them.
|
|
$stableAsset = New-ReleaseAssetName -Prefix "mesh-llm" -TargetTriple $targetTriple -ArchiveExt $archiveExt -BinaryFlavor $releaseFlavor
|
|
$versionedAsset = New-ReleaseAssetName -Prefix "mesh-llm-$Version" -TargetTriple $targetTriple -ArchiveExt $archiveExt -BinaryFlavor $releaseFlavor
|
|
|
|
$meshBinary = Join-Path $releaseBinDir "mesh-llm.exe"
|
|
|
|
if (-not (Test-HasValue $precomposedProductDir)) {
|
|
Require-File $meshBinary
|
|
}
|
|
|
|
$resolvedOutputDir = if ([System.IO.Path]::IsPathRooted($OutputDir)) {
|
|
[System.IO.Path]::GetFullPath($OutputDir)
|
|
} else {
|
|
[System.IO.Path]::GetFullPath((Join-Path $repoRoot $OutputDir))
|
|
}
|
|
New-Item -ItemType Directory -Path $resolvedOutputDir -Force | Out-Null
|
|
|
|
$stagingRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("mesh-llm-release-" + [System.Guid]::NewGuid().ToString("N"))
|
|
$bundleDir = Join-Path $stagingRoot "mesh-bundle"
|
|
New-Item -ItemType Directory -Path $bundleDir -Force | Out-Null
|
|
|
|
try {
|
|
if (Test-HasValue $precomposedProductDir) {
|
|
Copy-AndVerifyPrecomposedProduct `
|
|
-SourceDir $precomposedProductDir `
|
|
-BundleDir $bundleDir `
|
|
-ExpectedVersion $Version `
|
|
-ExpectedBackend $releaseFlavor `
|
|
-VerificationReport (Join-Path $stagingRoot "host-imports.verify.json")
|
|
} else {
|
|
$bundleBinary = Join-Path $bundleDir (Get-BundleBinaryName "mesh-llm" $binaryFlavor)
|
|
Copy-Item $meshBinary -Destination $bundleBinary -Force
|
|
Assert-MeshBinaryVersion -Path $bundleBinary -ExpectedVersion $Version
|
|
|
|
Invoke-ReleaseAttestationStamp -BinaryPath $bundleBinary
|
|
$python = Get-PythonCommand
|
|
$hostReport = Join-Path $bundleDir "host-imports.json"
|
|
& $python (Join-Path $scriptDir "verify-host-dependencies.py") $bundleBinary --report $hostReport
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "backend-neutral host dependency verification failed"
|
|
}
|
|
|
|
$cudaMajor = if ($env:MESH_LLM_CUDA_TOOLKIT_MAJOR) {
|
|
$env:MESH_LLM_CUDA_TOOLKIT_MAJOR
|
|
} elseif ($env:MESH_CUDA_VERSION) {
|
|
($env:MESH_CUDA_VERSION -split '\.')[0]
|
|
} else {
|
|
""
|
|
}
|
|
$selectorArgs = @(
|
|
(Join-Path $scriptDir "select-native-runtime.py")
|
|
"--root"
|
|
$nativeRuntimeRoot
|
|
"--os"
|
|
"windows"
|
|
"--arch"
|
|
"x86_64"
|
|
"--backend"
|
|
$releaseFlavor
|
|
)
|
|
if (Test-HasValue $cudaMajor) {
|
|
$selectorArgs += @("--cuda-major", $cudaMajor)
|
|
}
|
|
$selectorOutput = & $python @selectorArgs
|
|
$selectorExitCode = $LASTEXITCODE
|
|
if ($selectorExitCode -ne 0) {
|
|
throw "failed to select the packaged Windows native runtime"
|
|
}
|
|
$runtimeDir = $selectorOutput | ForEach-Object { $_.Trim() } | Where-Object { $_ } | Select-Object -Last 1
|
|
if (-not $runtimeDir) {
|
|
throw "failed to select the packaged Windows native runtime"
|
|
}
|
|
$runtimeDestinationRoot = Join-Path $bundleDir "native-runtimes"
|
|
$runtimeDestination = Join-Path $runtimeDestinationRoot (Split-Path -Leaf $runtimeDir)
|
|
New-Item -ItemType Directory -Path $runtimeDestinationRoot -Force | Out-Null
|
|
Copy-Item $runtimeDir -Destination $runtimeDestination -Recurse -Force
|
|
|
|
& $python (Join-Path $scriptDir "compose-product-bundle.py") `
|
|
--bundle $bundleDir `
|
|
--host $bundleBinary `
|
|
--runtime $runtimeDestination `
|
|
--version $Version `
|
|
--backend $releaseFlavor
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "failed to write the product-v2 bundle manifest"
|
|
}
|
|
}
|
|
|
|
$versionedPath = Join-Path $resolvedOutputDir $versionedAsset
|
|
$stablePath = Join-Path $resolvedOutputDir $stableAsset
|
|
|
|
New-ZipArchive -SourceDir $bundleDir -ArchivePath $versionedPath
|
|
New-ChecksumSidecar -Path $versionedPath
|
|
New-ZipArchive -SourceDir $bundleDir -ArchivePath $stablePath
|
|
New-ChecksumSidecar -Path $stablePath
|
|
|
|
Write-Host "Created release archives:"
|
|
Get-ChildItem -Path $resolvedOutputDir -File | Sort-Object Name | ForEach-Object {
|
|
Write-Host $_.FullName
|
|
}
|
|
} finally {
|
|
if (Test-Path $stagingRoot) {
|
|
Remove-Item $stagingRoot -Recurse -Force
|
|
}
|
|
}
|