$ErrorActionPreference = "Stop" $root = Split-Path -Parent $MyInvocation.MyCommand.Path $releaseRoot = Join-Path $root "release" $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $basePackageName = "domaincheck_release_$timestamp" $packageName = $basePackageName $stagingRoot = Join-Path $releaseRoot $packageName $zipPath = Join-Path $releaseRoot "$packageName.zip" $hashPath = Join-Path $releaseRoot "$packageName.sha256.txt" $suffix = 1 while ((Test-Path -LiteralPath $stagingRoot) -or (Test-Path -LiteralPath $zipPath) -or (Test-Path -LiteralPath $hashPath)) { $packageName = "${basePackageName}_$suffix" $stagingRoot = Join-Path $releaseRoot $packageName $zipPath = Join-Path $releaseRoot "$packageName.zip" $hashPath = Join-Path $releaseRoot "$packageName.sha256.txt" $suffix += 1 } if (-not $releaseCommitSha) { $gitCommand = Get-Command git -ErrorAction SilentlyContinue if ($gitCommand) { try { $insideRepo = (& git -C $root rev-parse --is-inside-work-tree 2>$null).Trim() if ($insideRepo -eq "true") { $releaseCommitSha = (& git -C $root rev-parse --short=12 HEAD 2>$null).Trim() $releaseCommitRef = (& git -C $root rev-parse --abbrev-ref HEAD 2>$null).Trim() } } catch { } } } $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 { "" } $releaseCommitSha = if ($env:DOMAINCHECK_RELEASE_COMMIT_SHA) { $env:DOMAINCHECK_RELEASE_COMMIT_SHA } else { $releaseCommitSha } $releaseCommitRef = if ($env:DOMAINCHECK_RELEASE_COMMIT_REF) { $env:DOMAINCHECK_RELEASE_COMMIT_REF } else { $releaseCommitRef } 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", "package_domain_release.sh", "verify_domain_release.sh", "prepare_final_release.sh", "show_latest_release.sh" ) 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 $domainCheckTarget = Join-Path $stagingRoot "domainCheck" Copy-OptionalItem -Source (Join-Path $root "domainCheck") -Destination $stagingRoot if (Test-Path -LiteralPath $domainCheckTarget) { $excludedDomainCheckPaths = @( ".venv", ".pytest_cache", "__pycache__", "logs", "data", "runtime", ".env", "credentials.json", "juming_cookies.pkl", "detect_options.json", "proxy_config.json", "thread_count.json", "node_thread_counts.json", "process_count.json", "node_process_counts.json", "runtime_settings.json", "detect_worker.log" ) foreach ($relativePath in $excludedDomainCheckPaths) { $targetPath = Join-Path $domainCheckTarget $relativePath if (Test-Path -LiteralPath $targetPath) { Remove-Item -LiteralPath $targetPath -Recurse -Force } } Get-ChildItem -Path $domainCheckTarget -Directory -Recurse -Force -Filter "__pycache__" | ForEach-Object { Remove-Item -LiteralPath $_.FullName -Recurse -Force } Get-ChildItem -Path $domainCheckTarget -File -Recurse -Force -Include "*.pyc", "detect_worker*.log" | ForEach-Object { Remove-Item -LiteralPath $_.FullName -Force } } $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 } catch { $smokeMessage = $_.Exception.Message } } else { $smokeMessage = "smoke test runtime not found" } if (Test-Path -LiteralPath $smokeOutput) { try { $smokeReport = Get-Content -LiteralPath $smokeOutput -Raw | ConvertFrom-Json $checks = @($smokeReport.checks) $totalChecks = $checks.Count $failedChecks = @($checks | Where-Object { -not $_.ok } | ForEach-Object { [string]$_.name }) $passedChecks = $totalChecks - $failedChecks.Count $smokeOk = [bool]$smokeReport.ok if ($smokeOk) { $smokeMessage = if ($totalChecks -gt 0) { "all checks passed ($passedChecks/$totalChecks)" } else { "all checks passed" } } else { $failedPreview = ($failedChecks | Select-Object -First 5) -join ", " $smokeMessage = if ($failedPreview) { "$passedChecks/$totalChecks checks passed; failed: $failedPreview" } else { "$passedChecks/$totalChecks checks passed" } } } catch { if (-not $smokeMessage) { $smokeMessage = "smoke report unreadable" } } } $manifest = [ordered]@{ package_name = $packageName package_type = "zip" generated_at = (Get-Date).ToString("s") root = $root commit_sha = $releaseCommitSha commit_ref = $releaseCommitRef 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 domain_check = if (Test-Path -LiteralPath $domainCheckTarget) { Get-ChildItem -Path $domainCheckTarget | Select-Object -ExpandProperty Name } else { @() } } } $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", "- domainCheck", "- 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 package_type = "zip" generated_at = (Get-Date).ToString("s") staging_path = $stagingRoot archive_path = $zipPath zip_path = $zipPath sha256_path = $hashPath sha256 = $zipHash smoke_test_ok = $smokeOk smoke_test_message = $smokeMessage smoke_test_report = if (Test-Path -LiteralPath $smokeOutput) { "smoke_test_report.json" } else { "" } commit_sha = $releaseCommitSha commit_ref = $releaseCommitRef } $latestRelease | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $latestJsonPath -Encoding UTF8 @( "package_name=$packageName" "staging_path=$stagingRoot" "archive_path=$zipPath" "zip_path=$zipPath" "sha256_path=$hashPath" "sha256=$zipHash" "smoke_test_ok=$smokeOk" "commit_sha=$releaseCommitSha" "commit_ref=$releaseCommitRef" ) | 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"