Files
story/scripts/build-server-deployment-bundle.ps1
T
Codex Local bf1233d5df
ci / verify (push) Canceled after 0s
feat: expand companion platform, device controls, and memoir workflows
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
2026-10-08 10:06:44 +08:00

88 lines
5.1 KiB
PowerShell

param(
[string]$Version = '0.76.0-rc.hardware1',
[string]$OutputDirectory = '.deploy-artifacts'
)
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
$projectRoot = (Resolve-Path -LiteralPath (Split-Path -Parent $PSScriptRoot)).Path
$outputRoot = [IO.Path]::GetFullPath((Join-Path $projectRoot $OutputDirectory))
if (-not $outputRoot.StartsWith($projectRoot + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) {
throw 'OutputDirectory must resolve inside the project workspace'
}
if ($Version -notmatch '^[0-9A-Za-z][0-9A-Za-z._-]{0,79}$') {
throw 'Version contains unsupported characters'
}
New-Item -ItemType Directory -Force -Path $outputRoot | Out-Null
$bundleName = "data-terminal-cloud-$Version"
$stagingRoot = Join-Path $outputRoot ('.staging-' + [Guid]::NewGuid().ToString('N'))
$payloadRoot = Join-Path $stagingRoot $bundleName
New-Item -ItemType Directory -Force -Path $payloadRoot | Out-Null
try {
$candidatePaths = @(& git -c core.quotePath=false ls-files --cached --others --exclude-standard -- `
cmd internal migrations openapi web deploy scripts test docs compose.yaml go.mod go.sum README.md CHANGELOG.md .env.example .dockerignore)
if ($LASTEXITCODE -ne 0) { throw 'git file enumeration failed' }
$candidatePaths = $candidatePaths | Where-Object {
$_ -and
$_ -notmatch '(^|/)(node_modules|dist|coverage|runtime|release)(/|$)' -and
$_ -notmatch '(^|/)\.env$' -and
$_ -notmatch '\.(pem|key|p12|pfx)$'
} | Sort-Object -Unique
if ($candidatePaths.Count -lt 20) { throw 'deployment file set is unexpectedly small' }
foreach ($relativePath in $candidatePaths) {
$sourcePath = [IO.Path]::GetFullPath((Join-Path $projectRoot $relativePath))
if (-not $sourcePath.StartsWith($projectRoot + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase) -or -not (Test-Path -LiteralPath $sourcePath -PathType Leaf)) {
throw "Unsafe or missing deployment source path: $relativePath"
}
$targetPath = Join-Path $payloadRoot $relativePath
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $targetPath) | Out-Null
Copy-Item -LiteralPath $sourcePath -Destination $targetPath
}
$sourceCommit = (& git rev-parse HEAD).Trim()
if ($LASTEXITCODE -ne 0 -or $sourceCommit -notmatch '^[0-9a-f]{40}$') { throw 'source commit could not be resolved' }
$statusLines = @(& git status --porcelain=v1)
if ($LASTEXITCODE -ne 0) { throw 'working tree status could not be resolved' }
$files = Get-ChildItem -LiteralPath $payloadRoot -File -Recurse | Sort-Object FullName | ForEach-Object {
[ordered]@{
path = $_.FullName.Substring($payloadRoot.Length + 1).Replace('\','/')
size_bytes = $_.Length
sha256 = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
}
}
$manifest = [ordered]@{
schema_version = 'server-deployment-bundle-v1'
candidate_version = $Version
generated_at = [DateTime]::UtcNow.ToString('o')
source_commit = $sourceCommit
working_tree_dirty = $statusLines.Count -gt 0
schema_migration_target = [int](($candidatePaths | Where-Object { $_ -match '^migrations/[0-9]+_.+\.up\.sql$' } | ForEach-Object { [int](($_ -split '/')[1] -split '_')[0] } | Measure-Object -Maximum).Maximum)
hardware_runtime = 'volc_hardware_agent'
files = @($files)
}
$manifestPath = Join-Path $payloadRoot 'deploy-manifest.json'
[IO.File]::WriteAllText($manifestPath, (($manifest | ConvertTo-Json -Depth 8) + [Environment]::NewLine), [Text.UTF8Encoding]::new($false))
$checksumLines = Get-ChildItem -LiteralPath $payloadRoot -File -Recurse | Where-Object Name -ne 'SHA256SUMS' | Sort-Object FullName | ForEach-Object {
$relative = $_.FullName.Substring($payloadRoot.Length + 1).Replace('\','/')
"{0} {1}" -f (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant(), $relative
}
[IO.File]::WriteAllLines((Join-Path $payloadRoot 'SHA256SUMS'), [string[]]$checksumLines, [Text.UTF8Encoding]::new($false))
$archivePath = Join-Path $outputRoot ($bundleName + '.zip')
if (Test-Path -LiteralPath $archivePath) { Remove-Item -LiteralPath $archivePath -Force }
Compress-Archive -LiteralPath $payloadRoot -DestinationPath $archivePath -CompressionLevel Optimal
$archiveHash = (Get-FileHash -LiteralPath $archivePath -Algorithm SHA256).Hash.ToLowerInvariant()
"$archiveHash $([IO.Path]::GetFileName($archivePath))" | Set-Content -LiteralPath ($archivePath + '.sha256') -Encoding ascii
[ordered]@{ archive = $archivePath; sha256 = $archiveHash; files = $files.Count; source_commit = $sourceCommit; working_tree_dirty = $statusLines.Count -gt 0 } | ConvertTo-Json -Compress
} finally {
$resolvedStaging = [IO.Path]::GetFullPath($stagingRoot)
if ($resolvedStaging.StartsWith($outputRoot + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase) -and (Test-Path -LiteralPath $resolvedStaging)) {
Remove-Item -LiteralPath $resolvedStaging -Recurse -Force
}
}