$ErrorActionPreference = "Stop" $root = Split-Path -Parent $MyInvocation.MyCommand.Path $releaseRoot = Join-Path $root "release" $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $packageName = "domaincheck_release_$timestamp" $stagingRoot = Join-Path $releaseRoot $packageName $zipPath = Join-Path $releaseRoot "$packageName.zip" $hashPath = Join-Path $releaseRoot "$packageName.sha256.txt" $latestTxtPath = Join-Path $releaseRoot "latest_release.txt" $latestJsonPath = Join-Path $releaseRoot "latest_release.json" $smokeScript = Join-Path $root "domainCheck\.venv\Scripts\python.exe" $smokeRunner = Join-Path $root "domain-api\deploy\linux\smoke_test.py" $smokeOutput = Join-Path $stagingRoot "smoke_test_report.json" $apiBaseUrl = if ($env:API_BASE_URL) { $env:API_BASE_URL } else { "http://127.0.0.1:8100" } $webUrl = if ($env:WEB_URL) { $env:WEB_URL } else { "http://127.0.0.1:3200" } function Copy-OptionalItem { param( [string]$Source, [string]$Destination ) if (Test-Path -LiteralPath $Source) { Copy-Item -LiteralPath $Source -Destination $Destination -Recurse -Force } } New-Item -ItemType Directory -Force -Path $releaseRoot | Out-Null if (Test-Path -LiteralPath $stagingRoot) { Remove-Item -LiteralPath $stagingRoot -Recurse -Force } if (Test-Path -LiteralPath $zipPath) { Remove-Item -LiteralPath $zipPath -Force } if (Test-Path -LiteralPath $hashPath) { Remove-Item -LiteralPath $hashPath -Force } New-Item -ItemType Directory -Force -Path $stagingRoot | Out-Null $docsTarget = Join-Path $stagingRoot "docs" New-Item -ItemType Directory -Force -Path $docsTarget | Out-Null Copy-Item -Path (Join-Path $root "docs\*.md") -Destination $docsTarget -Force $scriptsTarget = Join-Path $stagingRoot "scripts" New-Item -ItemType Directory -Force -Path $scriptsTarget | Out-Null $rootScripts = @( "start_domain_api.ps1", "stop_domain_api.ps1", "start_domain_web.ps1", "stop_domain_web.ps1", "start_domain_web_background.ps1", "start_domain_stack.ps1", "stop_domain_stack.ps1", "smoke_test_stack.ps1", "package_domain_release.ps1", "verify_domain_release.ps1", "prepare_final_release.ps1", "show_latest_release.ps1" ) foreach ($script in $rootScripts) { Copy-OptionalItem -Source (Join-Path $root $script) -Destination $scriptsTarget } $apiTarget = Join-Path $stagingRoot "domain-api" New-Item -ItemType Directory -Force -Path $apiTarget | Out-Null $apiItems = @( "app", "deploy", "README.md", "requirements.txt", ".env.example" ) foreach ($item in $apiItems) { Copy-OptionalItem -Source (Join-Path $root "domain-api\$item") -Destination $apiTarget } $webTarget = Join-Path $stagingRoot "domain-web" New-Item -ItemType Directory -Force -Path $webTarget | Out-Null $webItems = @( "src", "deploy", "README.md", "package.json", "package-lock.json", "tsconfig.json", "tsconfig.node.json", "vite.config.ts", ".env.example", ".env.production.example" ) foreach ($item in $webItems) { Copy-OptionalItem -Source (Join-Path $root "domain-web\$item") -Destination $webTarget } Copy-OptionalItem -Source (Join-Path $root "domain-web\dist") -Destination $webTarget $smokeOk = $false $smokeMessage = "" if ((Test-Path -LiteralPath $smokeScript) -and (Test-Path -LiteralPath $smokeRunner)) { try { & $smokeScript $smokeRunner --base-url $apiBaseUrl --web-url $webUrl --output $smokeOutput | Out-Null if (Test-Path -LiteralPath $smokeOutput) { $smokeOk = $true $smokeMessage = "generated" } } catch { $smokeMessage = $_.Exception.Message } } else { $smokeMessage = "smoke test runtime not found" } $manifest = [ordered]@{ package_name = $packageName generated_at = (Get-Date).ToString("s") root = $root smoke_test = [ordered]@{ ok = $smokeOk message = $smokeMessage api_base_url = $apiBaseUrl web_url = $webUrl report = if ($smokeOk) { "smoke_test_report.json" } else { "" } } included = [ordered]@{ docs = Get-ChildItem -Path $docsTarget -File | Select-Object -ExpandProperty Name scripts = Get-ChildItem -Path $scriptsTarget -File | Select-Object -ExpandProperty Name domain_api = Get-ChildItem -Path $apiTarget | Select-Object -ExpandProperty Name domain_web = Get-ChildItem -Path $webTarget | Select-Object -ExpandProperty Name } } $manifestPath = Join-Path $stagingRoot "release_manifest.json" $manifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $manifestPath -Encoding UTF8 $readmePath = Join-Path $stagingRoot "README_RELEASE.txt" @( "domainCheck release package", "generated_at=$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')", "", "Contents:", "- docs", "- scripts", "- domain-api", "- domain-web", "- release_manifest.json", "- smoke_test_report.json (if generated)", "", "Recommended order:", "1. Read docs\08_domainCheck_交付说明.md", "2. Read docs\05_domainCheck_Linux部署清单.md", "3. Review release_manifest.json and smoke_test_report.json", "4. On Windows run scripts\smoke_test_stack.ps1", "5. On Linux read domain-api\deploy\linux\README.md" ) | Set-Content -LiteralPath $readmePath -Encoding UTF8 Compress-Archive -Path (Join-Path $stagingRoot "*") -DestinationPath $zipPath -Force $zipHash = (Get-FileHash -LiteralPath $zipPath -Algorithm SHA256).Hash.ToLowerInvariant() @( "algorithm=SHA256" "file=$($packageName).zip" "sha256=$zipHash" ) | Set-Content -LiteralPath $hashPath -Encoding UTF8 $latestRelease = [ordered]@{ package_name = $packageName generated_at = (Get-Date).ToString("s") staging_path = $stagingRoot zip_path = $zipPath sha256_path = $hashPath sha256 = $zipHash smoke_test_ok = $smokeOk } $latestRelease | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $latestJsonPath -Encoding UTF8 @( "package_name=$packageName" "staging_path=$stagingRoot" "zip_path=$zipPath" "sha256_path=$hashPath" "sha256=$zipHash" "smoke_test_ok=$smokeOk" ) | Set-Content -LiteralPath $latestTxtPath -Encoding UTF8 Write-Host "release package created" Write-Host "staging: $stagingRoot" Write-Host "zip: $zipPath" Write-Host "sha256: $hashPath" Write-Host "latest: $latestJsonPath" Write-Host "smoke_test_ok: $smokeOk"