初版功能完成
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
[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"
|
||||
Reference in New Issue
Block a user