- Add publish.ps1 and build-release.ps1 for publishing to Gitea - Add dockmv logo assets (full, small, very small variants) - Update Makefile with publish and publish-binaries targets - Support PowerShell-based release pipeline with Gitea integration Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
229 lines
8.3 KiB
PowerShell
229 lines
8.3 KiB
PowerShell
#requires -Version 5.1
|
|
<#
|
|
.SYNOPSIS
|
|
Build the docker-migrate web UI and cross-compile release binaries.
|
|
|
|
.DESCRIPTION
|
|
Produces dist/docker-migrate-<os>-<arch>[.exe] for every target, one archive
|
|
per binary (.zip for Windows, .tar.gz elsewhere) and dist/SHA256SUMS.txt.
|
|
|
|
The PowerShell equivalent of `make release`, so a Windows host without make
|
|
can cut the same artifacts. The web UI is built first: vite writes into
|
|
internal/webui/dist, which the Go binary embeds, so the UI must be current
|
|
before the compile step — that directory is committed, so -SkipUi is safe for
|
|
a backend-only iteration.
|
|
|
|
Runs anywhere: the Go toolchain cross-compiles with CGO_ENABLED=0, and no
|
|
target needs a platform-specific linker.
|
|
|
|
.EXAMPLE
|
|
./scripts/build-release.ps1
|
|
Build every default target, tagging archives with `git describe`.
|
|
|
|
.EXAMPLE
|
|
./scripts/build-release.ps1 -Tag v1.2.0 -Targets windows/amd64, linux/amd64
|
|
Build two targets only, named v1.2.0.
|
|
|
|
.EXAMPLE
|
|
./scripts/build-release.ps1 -SkipUi
|
|
Reuse the committed internal/webui/dist instead of running npm.
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
# os/arch pairs to build. Anything `go tool dist list` accepts works.
|
|
[string[]]$Targets = @("linux/amd64", "linux/arm64", "darwin/arm64", "darwin/amd64", "windows/amd64"),
|
|
|
|
# Version stamped into the binary and used in archive names. Defaults to
|
|
# `git describe --tags --always --dirty`, matching the Makefile.
|
|
[string]$Tag,
|
|
|
|
# Go toolchain to build with.
|
|
[string]$Go = "go",
|
|
|
|
# Skip the `npm ci && npm run build` UI step.
|
|
[switch]$SkipUi,
|
|
|
|
# Remove dist/ before building.
|
|
[switch]$Clean,
|
|
|
|
# Build the binaries but skip archives and checksums.
|
|
[switch]$NoArchive
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Invoke-Checked {
|
|
param([Parameter(Mandatory)][string]$Exe, [Parameter(Mandatory)][string[]]$Args)
|
|
Write-Host " > $Exe $($Args -join ' ')" -ForegroundColor DarkGray
|
|
& $Exe @Args
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "'$Exe $($Args -join ' ')' failed with exit code $LASTEXITCODE."
|
|
}
|
|
}
|
|
|
|
function Invoke-GoBuild {
|
|
<#
|
|
One cross-compile. GOOS/GOARCH/CGO_ENABLED are process-wide env vars, so
|
|
they are saved and restored: leaving GOOS=windows set would silently
|
|
poison every later `go` call in the same session.
|
|
#>
|
|
param(
|
|
[Parameter(Mandatory)][string]$Go,
|
|
[Parameter(Mandatory)][string]$Goos,
|
|
[Parameter(Mandatory)][string]$Goarch,
|
|
[Parameter(Mandatory)][string]$Output,
|
|
[Parameter(Mandatory)][string]$Ldflags
|
|
)
|
|
|
|
$saved = @{
|
|
GOOS = $env:GOOS
|
|
GOARCH = $env:GOARCH
|
|
CGO_ENABLED = $env:CGO_ENABLED
|
|
}
|
|
try {
|
|
$env:GOOS = $Goos
|
|
$env:GOARCH = $Goarch
|
|
$env:CGO_ENABLED = "0"
|
|
Invoke-Checked $Go @("build", "-trimpath", "-ldflags", $Ldflags, "-o", $Output, ".")
|
|
}
|
|
finally {
|
|
foreach ($k in $saved.Keys) {
|
|
if ($null -eq $saved[$k]) { Remove-Item "env:$k" -ErrorAction SilentlyContinue }
|
|
else { Set-Item "env:$k" $saved[$k] }
|
|
}
|
|
}
|
|
}
|
|
|
|
function New-Archive {
|
|
<#
|
|
Archive one binary. .zip for Windows consumers, .tar.gz for the unix
|
|
targets — a tar preserves the executable bit, which a zip made by
|
|
Compress-Archive does not, so a downloaded linux binary stays runnable
|
|
without a chmod.
|
|
#>
|
|
param(
|
|
[Parameter(Mandatory)][string]$BinaryPath,
|
|
[Parameter(Mandatory)][string]$ArchivePath
|
|
)
|
|
|
|
if (Test-Path $ArchivePath) { Remove-Item -Force $ArchivePath }
|
|
$dir = Split-Path -Parent $BinaryPath
|
|
$name = Split-Path -Leaf $BinaryPath
|
|
|
|
if ($ArchivePath.EndsWith(".zip")) {
|
|
Compress-Archive -Path $BinaryPath -DestinationPath $ArchivePath
|
|
}
|
|
else {
|
|
# bsdtar ships with Windows 10 1803+; -C keeps the archive flat so it
|
|
# extracts as a bare binary rather than a dist/ tree.
|
|
$tar = (Get-Command tar -ErrorAction SilentlyContinue).Source
|
|
if (-not $tar) { throw "tar not found — needed for .tar.gz archives (pass -NoArchive to skip)." }
|
|
Invoke-Checked $tar @("-czf", $ArchivePath, "-C", $dir, $name)
|
|
}
|
|
}
|
|
|
|
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
Push-Location $repoRoot
|
|
try {
|
|
if (-not $Tag) {
|
|
try { $Tag = (git describe --tags --always --dirty 2>$null).Trim() } catch { }
|
|
if (-not $Tag) { $Tag = "dev" }
|
|
}
|
|
|
|
$bin = "docker-migrate"
|
|
$distDir = Join-Path $repoRoot "dist"
|
|
$ldflags = "-s -w -X main.version=$Tag"
|
|
|
|
Write-Host "docker-migrate release build" -ForegroundColor Cyan
|
|
Write-Host " go : $Go"
|
|
Write-Host " tag : $Tag"
|
|
Write-Host " targets : $($Targets -join ', ')"
|
|
Write-Host " output : $distDir"
|
|
Write-Host ""
|
|
|
|
if ($Clean) {
|
|
Write-Host "Cleaning dist/..." -ForegroundColor Cyan
|
|
if (Test-Path $distDir) { Remove-Item -Recurse -Force $distDir }
|
|
}
|
|
New-Item -ItemType Directory -Force -Path $distDir | Out-Null
|
|
|
|
# --- Web UI ---------------------------------------------------------------
|
|
# vite's outDir is internal/webui/dist, which embed.go embeds, so this has to
|
|
# run before the compile — not after, and not in parallel with it.
|
|
if (-not $SkipUi) {
|
|
Write-Host "Building web UI..." -ForegroundColor Cyan
|
|
if (-not (Get-Command npm -ErrorAction SilentlyContinue)) {
|
|
throw "npm not found — install Node 22+, or pass -SkipUi to reuse the committed internal/webui/dist."
|
|
}
|
|
Push-Location (Join-Path $repoRoot "web")
|
|
try {
|
|
Invoke-Checked npm @("ci", "--no-audit", "--no-fund")
|
|
Invoke-Checked npm @("run", "build")
|
|
}
|
|
finally { Pop-Location }
|
|
Write-Host ""
|
|
}
|
|
|
|
$embedded = Join-Path $repoRoot "internal/webui/dist/index.html"
|
|
if (-not (Test-Path $embedded)) {
|
|
throw "internal/webui/dist/index.html is missing — the binary would serve an empty UI. Run without -SkipUi."
|
|
}
|
|
|
|
# --- Compile --------------------------------------------------------------
|
|
Write-Host "Cross-compiling..." -ForegroundColor Cyan
|
|
$built = @()
|
|
foreach ($target in $Targets) {
|
|
$parts = $target -split "/"
|
|
if ($parts.Count -ne 2) { throw "target '$target' is not in os/arch form." }
|
|
$goos, $goarch = $parts
|
|
|
|
$ext = if ($goos -eq "windows") { ".exe" } else { "" }
|
|
$binaryPath = Join-Path $distDir "$bin-$goos-$goarch$ext"
|
|
|
|
Write-Host " $goos/$goarch" -ForegroundColor DarkGray
|
|
Invoke-GoBuild -Go $Go -Goos $goos -Goarch $goarch -Output $binaryPath -Ldflags $ldflags
|
|
if (-not (Test-Path $binaryPath)) { throw "go build reported success but $binaryPath is missing." }
|
|
|
|
$archivePath = $null
|
|
if (-not $NoArchive) {
|
|
$archiveExt = if ($goos -eq "windows") { "zip" } else { "tar.gz" }
|
|
$archivePath = Join-Path $distDir "$bin-$Tag-$goos-$goarch.$archiveExt"
|
|
New-Archive -BinaryPath $binaryPath -ArchivePath $archivePath
|
|
}
|
|
|
|
$built += [pscustomobject]@{
|
|
Target = $target
|
|
Binary = $binaryPath
|
|
Archive = $archivePath
|
|
SizeMb = [math]::Round((Get-Item $binaryPath).Length / 1MB, 1)
|
|
}
|
|
}
|
|
Write-Host ""
|
|
|
|
# --- Checksums ------------------------------------------------------------
|
|
# Over the archives, since those are what a release serves. Written in the
|
|
# `sha256sum -c` format so it verifies on a linux host as-is.
|
|
$sumsPath = $null
|
|
if (-not $NoArchive) {
|
|
Write-Host "Writing checksums..." -ForegroundColor Cyan
|
|
$sumsPath = Join-Path $distDir "SHA256SUMS.txt"
|
|
$lines = foreach ($b in $built) {
|
|
$hash = (Get-FileHash -Algorithm SHA256 $b.Archive).Hash.ToLower()
|
|
"$hash $(Split-Path -Leaf $b.Archive)"
|
|
}
|
|
# ASCII with LF: a BOM or CRLF makes `sha256sum -c` reject the first line.
|
|
[System.IO.File]::WriteAllText($sumsPath, ($lines -join "`n") + "`n", [System.Text.ASCIIEncoding]::new())
|
|
Write-Host ""
|
|
}
|
|
|
|
Write-Host "Done. Built:" -ForegroundColor Green
|
|
foreach ($b in $built) {
|
|
Write-Host " $($b.Binary) ($($b.SizeMb) MB)" -ForegroundColor Green
|
|
if ($b.Archive) { Write-Host " $($b.Archive)" -ForegroundColor Green }
|
|
}
|
|
if ($sumsPath) { Write-Host " $sumsPath" -ForegroundColor Green }
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|