Files
RemLink/scripts/build-release.ps1
qsc20001102 142e5dc7d6
ci / Go checks (ubuntu-latest) (push) Has been cancelled
ci / Go checks (windows-latest) (push) Has been cancelled
初版功能完成
2026-08-29 13:12:17 +08:00

166 lines
7.2 KiB
PowerShell

[CmdletBinding()]
param(
[string]$Version = "1.0.0",
[string]$OutputDirectory = "dist"
)
$ErrorActionPreference = "Stop"
if ($Version -notmatch '^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$') {
throw "Version must be a semantic version without path characters"
}
$repository = Split-Path -Parent $PSScriptRoot
$releaseRoot = Join-Path $repository $OutputDirectory
$engineerName = "RemLink-Engineer-v$Version-windows-amd64"
$siteName = "RemLink-Site-v$Version-windows-amd64"
$serverName = "RemLink-Server-v$Version-linux-amd64"
$engineerRoot = Join-Path $releaseRoot $engineerName
$siteRoot = Join-Path $releaseRoot $siteName
$serverRoot = Join-Path $releaseRoot $serverName
$linuxRoot = Join-Path $serverRoot "linux-amd64"
$validationScriptsRoot = Join-Path $serverRoot "scripts/validation"
$validationDocsRoot = Join-Path $serverRoot "docs/validation"
$commit = "unknown"
try { $commit = (git -C $repository rev-parse --short HEAD 2>$null).Trim() } catch {}
if (-not $commit) { $commit = "unknown" }
$linkerFlags = "-s -w -X remlink/internal/version.Version=$Version -X remlink/internal/version.Commit=$commit"
function Write-PackageMetadata {
param(
[Parameter(Mandatory = $true)][string]$Root,
[Parameter(Mandatory = $true)][string]$Role,
[Parameter(Mandatory = $true)][string]$Target
)
[ordered]@{
version = $Version
role = $Role
commit = $commit
built_at = (Get-Date).ToUniversalTime().ToString("o")
go = (go version)
node = (node --version)
target = @($Target)
portable_data_root = if ($Role -in @("Engineer", "Site")) { "executable_directory" } else { $null }
wails_build_tags = if ($Role -eq "Engineer") { @("desktop", "production") } else { $null }
} | ConvertTo-Json -Depth 4 | Set-Content -Encoding utf8 (Join-Path $Root "BUILD-INFO.json")
$checksumLines = Get-ChildItem -LiteralPath $Root -Recurse -File | Sort-Object FullName | ForEach-Object {
$relativePath = [System.IO.Path]::GetRelativePath($Root, $_.FullName).Replace('\', '/')
$hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $_.FullName).Hash.ToLowerInvariant()
"$hash $relativePath"
}
$checksumLines | Set-Content -Encoding ascii (Join-Path $Root "SHA256SUMS.txt")
}
function New-PackageArchive {
param(
[Parameter(Mandatory = $true)][string]$Root,
[Parameter(Mandatory = $true)][string]$Name,
[Parameter(Mandatory = $true)][string]$Role
)
$archivePath = Join-Path $releaseRoot "$Name.zip"
if (Test-Path -LiteralPath $archivePath) {
Remove-Item -LiteralPath $archivePath -Force
}
Compress-Archive -Path $Root -DestinationPath $archivePath
& (Join-Path $repository "scripts/validation/Test-ReleasePackage.ps1") -PackagePath $archivePath -Role $Role
return $archivePath
}
New-Item -ItemType Directory -Force -Path $releaseRoot | Out-Null
$generatedTargets = @(
$engineerRoot, $siteRoot, $serverRoot,
(Join-Path $releaseRoot "RemLink-v$Version"),
(Join-Path $releaseRoot "RemLink-v$Version.zip")
)
foreach ($target in $generatedTargets) {
if (Test-Path -LiteralPath $target) {
Remove-Item -LiteralPath $target -Recurse -Force
}
}
New-Item -ItemType Directory -Force -Path (
$engineerRoot,
$siteRoot,
$linuxRoot,
(Join-Path $engineerRoot "docs"),
(Join-Path $siteRoot "docs"),
(Join-Path $engineerRoot "scripts/validation"),
(Join-Path $siteRoot "scripts/validation"),
(Join-Path $serverRoot "docs"),
$validationScriptsRoot,
$validationDocsRoot
) | Out-Null
Push-Location $repository
try {
& (Join-Path $repository "scripts/maintenance/Test-RepositoryHygiene.ps1")
npm ci --prefix frontend
npm run typecheck --prefix frontend
npm run build --prefix frontend
& (Join-Path $repository "scripts/validation/Test-FrontendProduction.ps1")
go mod verify
go test -count=1 ./...
go vet ./...
& (Join-Path $repository "scripts/validation/Test-Architecture.ps1")
& (Join-Path $repository "scripts/validation/Test-AcceptanceTools.ps1")
$savedGOOS = $env:GOOS
$savedGOARCH = $env:GOARCH
$savedCGO = $env:CGO_ENABLED
try {
$env:GOOS = "windows"
$env:GOARCH = "amd64"
$env:CGO_ENABLED = "0"
# Wails desktop production tags are mandatory. Without them Wails
# intentionally compiles app_default_windows.go, which only shows an
# error dialog instructing the operator to use `wails build`.
go build -trimpath -tags "desktop,production" -ldflags "$linkerFlags -H windowsgui" -o (Join-Path $engineerRoot "RemLinkEngineer.exe") ./cmd/engineer
go build -trimpath -ldflags $linkerFlags -o (Join-Path $siteRoot "RemLinkSite.exe") ./cmd/site
$env:GOOS = "linux"
$env:GOARCH = "amd64"
$env:CGO_ENABLED = "0"
go build -trimpath -ldflags $linkerFlags -o (Join-Path $linuxRoot "remlink-server") ./cmd/server
} finally {
$env:GOOS = $savedGOOS
$env:GOARCH = $savedGOARCH
$env:CGO_ENABLED = $savedCGO
}
# Engineer and Site are intentionally self-contained portable packages.
Copy-Item config/engineer.example.yaml (Join-Path $engineerRoot "engineer.yaml")
Copy-Item config/site.example.yaml (Join-Path $siteRoot "site.yaml")
Copy-Item THIRD_PARTY_NOTICES.md $engineerRoot
Copy-Item THIRD_PARTY_NOTICES.md $siteRoot
Copy-Item docs/packages/engineer-readme.md (Join-Path $engineerRoot "README.md")
Copy-Item docs/packages/site-readme.md (Join-Path $siteRoot "README.md")
Copy-Item docs/deployment-and-usage.md (Join-Path $engineerRoot "docs/deployment-and-usage.md")
Copy-Item docs/deployment-and-usage.md (Join-Path $siteRoot "docs/deployment-and-usage.md")
Copy-Item scripts/validation/Test-ReleasePackage.ps1 (Join-Path $engineerRoot "scripts/validation/Test-ReleasePackage.ps1")
Copy-Item scripts/validation/Test-ReleasePackage.ps1 (Join-Path $siteRoot "scripts/validation/Test-ReleasePackage.ps1")
# Server has its own native binary and Docker deployment context; it never
# contains either Windows client executable.
Copy-Item config/server.example.yaml (Join-Path $linuxRoot "server.yaml")
Copy-Item THIRD_PARTY_NOTICES.md $linuxRoot
Copy-Item README.md $serverRoot
Copy-Item -Recurse -Force deploy/docker (Join-Path $serverRoot "docker")
Copy-Item docs/deployment-and-usage.md (Join-Path $serverRoot "docs/deployment-and-usage.md")
Copy-Item docs/implementation-status.md (Join-Path $serverRoot "docs/implementation-status.md")
Copy-Item scripts/validation/* $validationScriptsRoot
Copy-Item docs/validation/* $validationDocsRoot
Write-PackageMetadata -Root $engineerRoot -Role Engineer -Target "windows/amd64"
Write-PackageMetadata -Root $siteRoot -Role Site -Target "windows/amd64"
Write-PackageMetadata -Root $serverRoot -Role Server -Target "linux/amd64"
$engineerArchive = New-PackageArchive -Root $engineerRoot -Name $engineerName -Role Engineer
$siteArchive = New-PackageArchive -Root $siteRoot -Name $siteName -Role Site
$serverArchive = New-PackageArchive -Root $serverRoot -Name $serverName -Role Server
} finally {
Pop-Location
}
Write-Host "Engineer package: $engineerArchive"
Write-Host "Site package: $siteArchive"
Write-Host "Server package: $serverArchive"