166 lines
7.2 KiB
PowerShell
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"
|