ci / verify (push) Canceled after 0s
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.
88 lines
5.1 KiB
PowerShell
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
|
|
}
|
|
}
|