!62 feat: 构建与发布脚本优化
Merge pull request !62 from Luke/feature/add-build-docker
This commit is contained in:
commit
d536bd17ce
2
.env
Normal file
2
.env
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Auto-generated by build\tools\generate-dotenv.ps1
|
||||||
|
SNOW_VERSION=0.7.0
|
||||||
6
.run/build-release-all.ps1.run.xml
Normal file
6
.run/build-release-all.ps1.run.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="build-release-all.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/build-release-all.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
|
||||||
|
<envs />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
6
.run/release-linux.ps1.run.xml
Normal file
6
.run/release-linux.ps1.run.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="release-linux.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/release-linux.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
|
||||||
|
<envs />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
6
.run/release-windows.ps1.run.xml
Normal file
6
.run/release-windows.ps1.run.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="release-windows.ps1" type="PowerShellRunType" factoryName="PowerShell" scriptUrl="$PROJECT_DIR$/build/release-windows.ps1" executablePath="C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe">
|
||||||
|
<envs />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
57
Dockerfile
Normal file
57
Dockerfile
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Stage 1: 官方 GraalVM 社区版(已含 native-image)
|
||||||
|
FROM ghcr.io/graalvm/native-image-community:24.0.2 AS builder
|
||||||
|
|
||||||
|
RUN microdnf install -y \
|
||||||
|
gcc gcc-c++ make git wget tar gzip which findutils maven \
|
||||||
|
&& microdnf clean all
|
||||||
|
|
||||||
|
# ---------- 构建 musl ----------
|
||||||
|
ARG MUSL_VER=1.2.5
|
||||||
|
WORKDIR /tmp
|
||||||
|
RUN wget -q https://musl.libc.org/releases/musl-${MUSL_VER}.tar.gz \
|
||||||
|
&& tar -xzf musl-${MUSL_VER}.tar.gz \
|
||||||
|
&& cd musl-${MUSL_VER} \
|
||||||
|
&& ./configure --prefix=/opt/musl-${MUSL_VER} --disable-shared \
|
||||||
|
&& make -j"$(nproc)" \
|
||||||
|
&& make install \
|
||||||
|
&& ln -s /opt/musl-${MUSL_VER} /opt/musl \
|
||||||
|
&& cd / && rm -rf /tmp/musl-${MUSL_VER}*
|
||||||
|
|
||||||
|
RUN ln -s /opt/musl/bin/musl-gcc /usr/local/bin/x86_64-linux-musl-gcc \
|
||||||
|
&& ln -s /opt/musl/bin/musl-gcc /usr/local/bin/x86_64-linux-musl-cc
|
||||||
|
|
||||||
|
ENV PATH="/opt/musl/bin:${PATH}"
|
||||||
|
ENV CC="musl-gcc"
|
||||||
|
ENV C_INCLUDE_PATH="/opt/musl/include"
|
||||||
|
ENV LIBRARY_PATH="/opt/musl/lib"
|
||||||
|
|
||||||
|
# ---------- 静态 zlib ----------
|
||||||
|
ARG ZLIB_VERSION=1.3.1
|
||||||
|
WORKDIR /tmp
|
||||||
|
RUN wget -q https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \
|
||||||
|
&& tar -xzf zlib-${ZLIB_VERSION}.tar.gz \
|
||||||
|
&& cd zlib-${ZLIB_VERSION} \
|
||||||
|
&& CC=musl-gcc ./configure --static --prefix=/opt/musl \
|
||||||
|
&& make -j"$(nproc)" \
|
||||||
|
&& make install \
|
||||||
|
&& cd / && rm -rf /tmp/zlib-${ZLIB_VERSION}*
|
||||||
|
|
||||||
|
# ---------- Maven 缓存优化 ----------
|
||||||
|
WORKDIR /app
|
||||||
|
COPY pom.xml ./
|
||||||
|
|
||||||
|
# 先拉依赖并缓存
|
||||||
|
RUN mvn -B -P native-linux dependency:go-offline
|
||||||
|
|
||||||
|
# ---------- 复制源码 ----------
|
||||||
|
COPY . /app
|
||||||
|
|
||||||
|
# ---------- 编译 native image ----------
|
||||||
|
RUN mvn -P native-linux -DskipTests clean package
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Stage 2: 输出产物镜像(可以直接 cp 出二进制)
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
FROM busybox AS export
|
||||||
|
WORKDIR /export
|
||||||
|
COPY --from=builder /app/org.jcnc.snow.cli.SnowCLI /export/Snow
|
||||||
48
build/build-project2tar.ps1
Normal file
48
build/build-project2tar.ps1
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Set the tar package name
|
||||||
|
$tarName = "Snow.tar"
|
||||||
|
|
||||||
|
# Get the script's current directory (build folder)
|
||||||
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
|
||||||
|
# Get the parent directory (the project root)
|
||||||
|
$parentDir = Split-Path -Parent $scriptDir
|
||||||
|
|
||||||
|
# Set the full path to the tar package
|
||||||
|
$tarPath = Join-Path $parentDir $tarName
|
||||||
|
|
||||||
|
# Output message: starting to create tar package
|
||||||
|
Write-Output "Starting to create tar package: $tarName in $parentDir ..."
|
||||||
|
|
||||||
|
# Remove old tar package if it exists
|
||||||
|
if (Test-Path $tarPath) {
|
||||||
|
Write-Output "Found an old $tarName, removing it..."
|
||||||
|
Remove-Item $tarPath -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make sure the tar command is available
|
||||||
|
$tarCommand = "tar"
|
||||||
|
if (-not (Get-Command $tarCommand -ErrorAction SilentlyContinue)) {
|
||||||
|
Write-Error "❌ 'tar' command is not available. Please make sure 'tar' is installed and can be run from PowerShell."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execute tar: change to org\jcnc directory and compress the snow folder
|
||||||
|
try {
|
||||||
|
# Build the command and run it
|
||||||
|
$tarCommandArgs = "-cf", $tarPath, "-C", "$scriptDir\..\src\main\java\org\jcnc", "snow"
|
||||||
|
Write-Output "Running tar command: tar $tarCommandArgs"
|
||||||
|
|
||||||
|
& $tarCommand @tarCommandArgs
|
||||||
|
} catch {
|
||||||
|
Write-Error "❌ Failed to create tar package. Error: $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if tar package was created successfully
|
||||||
|
if (Test-Path $tarPath) {
|
||||||
|
Write-Output "✅ Successfully created $tarName"
|
||||||
|
exit 0
|
||||||
|
} else {
|
||||||
|
Write-Error "❌ Creation failed. Please check the tar command and paths."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
129
build/build-release-all.ps1
Normal file
129
build/build-release-all.ps1
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
param(
|
||||||
|
[string]$LogDir = (Join-Path $PSScriptRoot 'target\parallel-logs')
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$winScript = Join-Path $PSScriptRoot 'release-windows.ps1'
|
||||||
|
$linScript = Join-Path $PSScriptRoot 'release-linux.ps1'
|
||||||
|
|
||||||
|
if (-not (Test-Path $winScript)) { throw "File not found: $winScript" }
|
||||||
|
if (-not (Test-Path $linScript)) { throw "File not found: $linScript" }
|
||||||
|
|
||||||
|
$winLogOut = [System.IO.Path]::GetTempFileName()
|
||||||
|
$winLogErr = [System.IO.Path]::GetTempFileName()
|
||||||
|
$linLogOut = [System.IO.Path]::GetTempFileName()
|
||||||
|
$linLogErr = [System.IO.Path]::GetTempFileName()
|
||||||
|
|
||||||
|
$winProc = Start-Process powershell.exe -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',"`"$winScript`"") `
|
||||||
|
-RedirectStandardOutput $winLogOut -RedirectStandardError $winLogErr -NoNewWindow -PassThru
|
||||||
|
$linProc = Start-Process powershell.exe -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',"`"$linScript`"") `
|
||||||
|
-RedirectStandardOutput $linLogOut -RedirectStandardError $linLogErr -NoNewWindow -PassThru
|
||||||
|
|
||||||
|
$winPosOut = 0
|
||||||
|
$winPosErr = 0
|
||||||
|
$linPosOut = 0
|
||||||
|
$linPosErr = 0
|
||||||
|
|
||||||
|
Write-Host "===== Build Started ====="
|
||||||
|
while (-not $winProc.HasExited -or -not $linProc.HasExited) {
|
||||||
|
# windows-release stdout
|
||||||
|
if (Test-Path $winLogOut) {
|
||||||
|
$size = (Get-Item $winLogOut).Length
|
||||||
|
if ($size -gt $winPosOut) {
|
||||||
|
$fs = [System.IO.File]::Open($winLogOut, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||||
|
$fs.Position = $winPosOut
|
||||||
|
$sr = New-Object System.IO.StreamReader($fs)
|
||||||
|
while (!$sr.EndOfStream) {
|
||||||
|
$line = $sr.ReadLine()
|
||||||
|
if ($line) { Write-Host "[windows-release][OUT] $line" }
|
||||||
|
}
|
||||||
|
$winPosOut = $fs.Position
|
||||||
|
$sr.Close()
|
||||||
|
$fs.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# windows-release stderr
|
||||||
|
if (Test-Path $winLogErr) {
|
||||||
|
$size = (Get-Item $winLogErr).Length
|
||||||
|
if ($size -gt $winPosErr) {
|
||||||
|
$fs = [System.IO.File]::Open($winLogErr, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||||
|
$fs.Position = $winPosErr
|
||||||
|
$sr = New-Object System.IO.StreamReader($fs)
|
||||||
|
while (!$sr.EndOfStream) {
|
||||||
|
$line = $sr.ReadLine()
|
||||||
|
if ($line) { Write-Host "[windows-release][ERR] $line" -ForegroundColor Red }
|
||||||
|
}
|
||||||
|
$winPosErr = $fs.Position
|
||||||
|
$sr.Close()
|
||||||
|
$fs.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# linux-release stdout
|
||||||
|
if (Test-Path $linLogOut) {
|
||||||
|
$size = (Get-Item $linLogOut).Length
|
||||||
|
if ($size -gt $linPosOut) {
|
||||||
|
$fs = [System.IO.File]::Open($linLogOut, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||||
|
$fs.Position = $linPosOut
|
||||||
|
$sr = New-Object System.IO.StreamReader($fs)
|
||||||
|
while (!$sr.EndOfStream) {
|
||||||
|
$line = $sr.ReadLine()
|
||||||
|
if ($line) { Write-Host "[linux-release][OUT] $line" }
|
||||||
|
}
|
||||||
|
$linPosOut = $fs.Position
|
||||||
|
$sr.Close()
|
||||||
|
$fs.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# linux-release stderr
|
||||||
|
if (Test-Path $linLogErr) {
|
||||||
|
$size = (Get-Item $linLogErr).Length
|
||||||
|
if ($size -gt $linPosErr) {
|
||||||
|
$fs = [System.IO.File]::Open($linLogErr, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||||
|
$fs.Position = $linPosErr
|
||||||
|
$sr = New-Object System.IO.StreamReader($fs)
|
||||||
|
while (!$sr.EndOfStream) {
|
||||||
|
$line = $sr.ReadLine()
|
||||||
|
if ($line) { Write-Host "[linux-release][ERR] $line" -ForegroundColor Red }
|
||||||
|
}
|
||||||
|
$linPosErr = $fs.Position
|
||||||
|
$sr.Close()
|
||||||
|
$fs.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Start-Sleep -Milliseconds 200
|
||||||
|
}
|
||||||
|
|
||||||
|
# After processes exit, print any remaining output
|
||||||
|
$tasks = @(
|
||||||
|
@{proc=$winProc; log=$winLogOut; tag='windows-release'; type='OUT'; skip=$winPosOut},
|
||||||
|
@{proc=$winProc; log=$winLogErr; tag='windows-release'; type='ERR'; skip=$winPosErr},
|
||||||
|
@{proc=$linProc; log=$linLogOut; tag='linux-release'; type='OUT'; skip=$linPosOut},
|
||||||
|
@{proc=$linProc; log=$linLogErr; tag='linux-release'; type='ERR'; skip=$linPosErr}
|
||||||
|
)
|
||||||
|
foreach ($item in $tasks) {
|
||||||
|
if (Test-Path $item.log) {
|
||||||
|
$fs = [System.IO.File]::Open($item.log, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
|
||||||
|
$fs.Position = $item.skip
|
||||||
|
$sr = New-Object System.IO.StreamReader($fs)
|
||||||
|
while (!$sr.EndOfStream) {
|
||||||
|
$line = $sr.ReadLine()
|
||||||
|
if ($line) {
|
||||||
|
if ($item.type -eq 'ERR') {
|
||||||
|
Write-Host "[$($item.tag)][ERR] $line" -ForegroundColor Red
|
||||||
|
} else {
|
||||||
|
Write-Host "[$($item.tag)][OUT] $line"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sr.Close()
|
||||||
|
$fs.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "All tasks completed successfully." -ForegroundColor Green
|
||||||
|
|
||||||
|
Remove-Item $winLogOut, $winLogErr, $linLogOut, $linLogErr -Force
|
||||||
|
exit 0
|
||||||
@ -1,47 +0,0 @@
|
|||||||
# 设定 tar 包的名称
|
|
||||||
$tarName = "Snow.tar"
|
|
||||||
|
|
||||||
# 获取脚本当前目录(build文件夹)
|
|
||||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
|
||||||
|
|
||||||
# 获取上一级目录(snow 根目录)
|
|
||||||
$parentDir = Split-Path -Parent $scriptDir
|
|
||||||
|
|
||||||
# 设置 tar 包的完整路径
|
|
||||||
$tarPath = Join-Path $parentDir $tarName
|
|
||||||
|
|
||||||
# 输出开始创建 tar 包的消息
|
|
||||||
Write-Output "开始创建 tar 包: $tarName 到 $parentDir ..."
|
|
||||||
|
|
||||||
# 如果存在旧 tar 包,先删除它
|
|
||||||
if (Test-Path $tarPath) {
|
|
||||||
Write-Output "发现旧的 $tarName,正在删除..."
|
|
||||||
Remove-Item $tarPath -Force
|
|
||||||
}
|
|
||||||
|
|
||||||
# 确保 tar 命令可用
|
|
||||||
$tarCommand = "tar"
|
|
||||||
if (-not (Get-Command $tarCommand -ErrorAction SilentlyContinue)) {
|
|
||||||
Write-Error "❌ tar 命令不可用。请确保 tar 已安装并可在 PowerShell 中执行。"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# 执行打包操作: 切换到 org\jcnc 目录下再压缩 snow 文件夹
|
|
||||||
try {
|
|
||||||
# 构建命令并执行
|
|
||||||
$tarCommandArgs = "-cf", $tarPath, "-C", "$scriptDir\..\src\main\java\org\jcnc", "snow"
|
|
||||||
Write-Output "执行 tar 命令: tar $tarCommandArgs"
|
|
||||||
|
|
||||||
& $tarCommand @tarCommandArgs
|
|
||||||
} catch {
|
|
||||||
Write-Error "❌ 创建 tar 包失败。错误信息: $_"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# 检查 tar 包是否创建成功
|
|
||||||
if (Test-Path $tarPath) {
|
|
||||||
Write-Output "✅ 成功创建 $tarName"
|
|
||||||
} else {
|
|
||||||
Write-Error "❌ 创建失败,请检查 tar 命令和路径是否正确。"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
55
build/release-linux.ps1
Normal file
55
build/release-linux.ps1
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# run-linux-snow-export.ps1
|
||||||
|
# Build and package linux-snow-export, version read from SNOW_VERSION in .env
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# Import shared dotenv parser function
|
||||||
|
. "$PSScriptRoot\tools\dotenv.ps1"
|
||||||
|
|
||||||
|
Write-Host "Step 0: Generate .env..."
|
||||||
|
try {
|
||||||
|
& "$PSScriptRoot\tools\generate-dotenv.ps1" -ErrorAction Stop
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to generate .env: $( $_.Exception.Message )"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Step 1: Build and run linux-snow-export..."
|
||||||
|
docker compose run --build --rm linux-snow-export
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Build & Run failed, exiting script."
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Step 2: Run linux-snow-export without rebuild..."
|
||||||
|
docker compose run --rm linux-snow-export
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Run without rebuild failed, exiting script."
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===== Step 3: Read version from .env =====
|
||||||
|
$projectRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||||
|
$dotenvPath = Join-Path $projectRoot ".env"
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $dotenvPath)) {
|
||||||
|
Write-Error ".env not found at: $dotenvPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = Read-DotEnvValue -FilePath $dotenvPath -Key 'SNOW_VERSION'
|
||||||
|
if (-not $version) {
|
||||||
|
Write-Error "SNOW_VERSION not found in .env"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===== Step 4: Define output paths =====
|
||||||
|
$targetDir = Join-Path $projectRoot "target\release"
|
||||||
|
$outDir = Join-Path $targetDir "Snow-v$version-linux-x64"
|
||||||
|
$tgzPath = Join-Path $targetDir "Snow-v$version-linux-x64.tgz"
|
||||||
|
|
||||||
|
Write-Host ">>> Package ready!" -ForegroundColor Green
|
||||||
|
Write-Host "Version : $version"
|
||||||
|
Write-Host "Output Dir : $outDir"
|
||||||
|
Write-Host "Tgz File : $tgzPath"
|
||||||
117
build/release-windows.ps1
Normal file
117
build/release-windows.ps1
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# release-windows.ps1
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$ProgressPreference = 'SilentlyContinue'
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
|
||||||
|
# Import shared dotenv parser function
|
||||||
|
. "$PSScriptRoot\tools\dotenv.ps1"
|
||||||
|
|
||||||
|
# ===== Utility Functions =====
|
||||||
|
function Find-PomUpwards([string]$startDir) {
|
||||||
|
$dir = Resolve-Path $startDir
|
||||||
|
while ($true) {
|
||||||
|
$pom = Join-Path $dir "pom.xml"
|
||||||
|
if (Test-Path $pom) { return $pom }
|
||||||
|
$parent = Split-Path $dir -Parent
|
||||||
|
if ($parent -eq $dir -or [string]::IsNullOrEmpty($parent)) { return $null }
|
||||||
|
$dir = $parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===== Step 0: Generate .env =====
|
||||||
|
Write-Host "Step 0: Generate .env..."
|
||||||
|
try {
|
||||||
|
& "$PSScriptRoot\tools\generate-dotenv.ps1" -ErrorAction Stop
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to generate .env: $($_.Exception.Message)"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===== Step 1: Locate project root & build =====
|
||||||
|
Write-Host "Step 1: Locate project root and build..."
|
||||||
|
$pom = Find-PomUpwards -startDir $PSScriptRoot
|
||||||
|
if (-not $pom) {
|
||||||
|
Write-Error "pom.xml not found. Please run this script within the project."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$projectRoot = Split-Path $pom -Parent
|
||||||
|
Push-Location $projectRoot
|
||||||
|
try {
|
||||||
|
Write-Host "→ Running: mvn clean package"
|
||||||
|
mvn clean package
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Maven build failed, exiting script."
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===== Step 2: Read SNOW_VERSION =====
|
||||||
|
Write-Host "Step 2: Read SNOW_VERSION from .env..."
|
||||||
|
$dotenvPath = Join-Path $projectRoot ".env"
|
||||||
|
$snowVersion = Read-DotEnvValue -FilePath $dotenvPath -Key "SNOW_VERSION"
|
||||||
|
if (-not $snowVersion) {
|
||||||
|
Write-Host "SNOW_VERSION not found in .env, using placeholder 0.0.0." -ForegroundColor Yellow
|
||||||
|
$snowVersion = "0.0.0"
|
||||||
|
}
|
||||||
|
Write-Host "SNOW_VERSION = $snowVersion"
|
||||||
|
|
||||||
|
# ===== Step 3: Prepare release directory structure =====
|
||||||
|
Write-Host "Step 3: Prepare release directory structure..."
|
||||||
|
$targetDir = Join-Path $projectRoot "target"
|
||||||
|
$exePath = Join-Path $targetDir "Snow.exe"
|
||||||
|
if (-not (Test-Path $exePath)) {
|
||||||
|
Write-Error "Expected build artifact not found: $exePath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$verName = "Snow-v${snowVersion}-windows-x64"
|
||||||
|
$releaseRoot = Join-Path $targetDir "release"
|
||||||
|
$outDir = Join-Path $releaseRoot $verName
|
||||||
|
$binDir = Join-Path $outDir "bin"
|
||||||
|
$libDir = Join-Path $outDir "lib"
|
||||||
|
|
||||||
|
# Clean old directory
|
||||||
|
if (Test-Path $outDir) {
|
||||||
|
Write-Host "→ Cleaning previous output directory..."
|
||||||
|
Remove-Item $outDir -Recurse -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Force -Path $binDir | Out-Null
|
||||||
|
Copy-Item -Path $exePath -Destination (Join-Path $binDir "Snow.exe") -Force
|
||||||
|
Write-Host ">>> Collected Snow.exe"
|
||||||
|
|
||||||
|
# Optional lib
|
||||||
|
$projectLib = Join-Path $projectRoot "lib"
|
||||||
|
if (Test-Path $projectLib) {
|
||||||
|
New-Item -ItemType Directory -Force -Path $libDir | Out-Null
|
||||||
|
Copy-Item -Path (Join-Path $projectLib "*") -Destination $libDir -Recurse -Force
|
||||||
|
Write-Host ">>> Copied lib directory"
|
||||||
|
} else {
|
||||||
|
Write-Host ">>> lib directory not found, skipping." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===== Step 4: Create zip =====
|
||||||
|
Write-Host "Step 4: Create release zip..."
|
||||||
|
New-Item -ItemType Directory -Force -Path $releaseRoot | Out-Null
|
||||||
|
$zipPath = Join-Path $releaseRoot ("{0}.zip" -f $verName)
|
||||||
|
if (Test-Path $zipPath) {
|
||||||
|
Write-Host "→ Removing existing zip: $zipPath"
|
||||||
|
Remove-Item $zipPath -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Compress-Archive -Path $outDir -DestinationPath $zipPath -Force
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to create zip: $($_.Exception.Message)"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ">>> Package ready!" -ForegroundColor Green
|
||||||
|
Write-Host "Version : $snowVersion"
|
||||||
|
Write-Host "Output Dir : $outDir"
|
||||||
|
Write-Host "Zip File : $zipPath"
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
53
build/tools/dotenv.ps1
Normal file
53
build/tools/dotenv.ps1
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# tools/dotenv.ps1
|
||||||
|
# Unified .env reader function:
|
||||||
|
# - Supports `KEY=VAL` and `export KEY=VAL`
|
||||||
|
# - Skips blank lines and comments
|
||||||
|
# - Handles quoted values (single or double quotes)
|
||||||
|
# - Allows inline comments at the end of a line (space + #)
|
||||||
|
# - If the same KEY is defined multiple times, the last one takes precedence
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
|
||||||
|
function Read-DotEnvValue {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)][string]$FilePath,
|
||||||
|
[Parameter(Mandatory=$true)][string]$Key
|
||||||
|
)
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $FilePath)) { return $null }
|
||||||
|
|
||||||
|
# Match the target key (escaped), allowing optional "export" prefix
|
||||||
|
$pattern = '^(?:\s*export\s+)?(?<k>' + [regex]::Escape($Key) + ')\s*=\s*(?<v>.*)$'
|
||||||
|
$value = $null
|
||||||
|
|
||||||
|
# Read line by line for large file compatibility
|
||||||
|
Get-Content -LiteralPath $FilePath | ForEach-Object {
|
||||||
|
$line = $_
|
||||||
|
|
||||||
|
# Skip blank lines and full-line comments
|
||||||
|
if ($line -match '^\s*$') { return }
|
||||||
|
if ($line -match '^\s*#') { return }
|
||||||
|
|
||||||
|
if ($line -match $pattern) {
|
||||||
|
$v = $matches['v']
|
||||||
|
|
||||||
|
# Remove surrounding quotes if present
|
||||||
|
$trimmed = $v.Trim()
|
||||||
|
if ($trimmed -match '^\s*"(.*)"\s*$') {
|
||||||
|
$v = $matches[1]
|
||||||
|
} elseif ($trimmed -match "^\s*'(.*)'\s*$") {
|
||||||
|
$v = $matches[1]
|
||||||
|
} else {
|
||||||
|
# Strip inline comments (space + # …), ignoring escaped \#
|
||||||
|
if ($v -match '^(.*?)(?<!\\)\s+#.*$') {
|
||||||
|
$v = $matches[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $v.Trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value
|
||||||
|
}
|
||||||
23
build/tools/generate-dotenv.ps1
Normal file
23
build/tools/generate-dotenv.ps1
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# build\tools\generate-dotenv.ps1
|
||||||
|
|
||||||
|
# Repository root: go up two levels from build\tools\
|
||||||
|
$repoRoot = (Get-Item $PSScriptRoot).Parent.Parent.FullName
|
||||||
|
$envPath = Join-Path $repoRoot ".env"
|
||||||
|
$pomPath = Join-Path $repoRoot "pom.xml"
|
||||||
|
|
||||||
|
if (-not (Test-Path $pomPath -PathType Leaf)) {
|
||||||
|
throw "pom.xml not found: $pomPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
[xml]$pom = Get-Content $pomPath -Encoding UTF8
|
||||||
|
$version = $pom.project.version
|
||||||
|
|
||||||
|
$lines = @(
|
||||||
|
"# Auto-generated by build\tools\generate-dotenv.ps1"
|
||||||
|
"SNOW_VERSION=$version"
|
||||||
|
)
|
||||||
|
|
||||||
|
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
||||||
|
[System.IO.File]::WriteAllLines($envPath, $lines, $utf8NoBom)
|
||||||
|
Write-Host "Generated/overwritten $envPath (version: $version)"
|
||||||
|
return
|
||||||
24
docker-compose.yml
Normal file
24
docker-compose.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
services:
|
||||||
|
# Run with: docker compose run --rm linux-snow-export
|
||||||
|
linux-snow-export:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
target: export
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -e
|
||||||
|
ver="Snow-v${SNOW_VERSION}-linux-x64"
|
||||||
|
mkdir -p "/output/release/$$ver/bin"
|
||||||
|
cp /export/Snow "/output/release/$$ver/bin/"
|
||||||
|
if [ -d /export/lib ]; then
|
||||||
|
mkdir -p "/output/release/$$ver/lib"
|
||||||
|
cp -a /export/lib/. "/output/release/$$ver/lib/"
|
||||||
|
fi
|
||||||
|
tar -C /output/release -czf "/output/release/$$ver.tgz" "$$ver"
|
||||||
|
volumes:
|
||||||
|
- ./target:/output
|
||||||
|
- ./lib:/export/lib:ro
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
46
pom.xml
46
pom.xml
@ -70,11 +70,7 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
<!--
|
<!-- 原生镜像构建: Linux 平台(使用 Docker builder 中的 musl 工具链) -->
|
||||||
原生镜像构建: Linux 平台
|
|
||||||
- 使用 GraalVM 的 native-image 工具,生成静态链接的可执行文件
|
|
||||||
- 依赖 musl libc,需提前安装并配置 musl-gcc 工具链
|
|
||||||
-->
|
|
||||||
<profile>
|
<profile>
|
||||||
<id>native-linux</id>
|
<id>native-linux</id>
|
||||||
<activation>
|
<activation>
|
||||||
@ -82,25 +78,39 @@
|
|||||||
<family>unix</family>
|
<family>unix</family>
|
||||||
</os>
|
</os>
|
||||||
</activation>
|
</activation>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.graalvm.buildtools</groupId>
|
<groupId>org.graalvm.buildtools</groupId>
|
||||||
<artifactId>native-maven-plugin</artifactId>
|
<artifactId>native-maven-plugin</artifactId>
|
||||||
<version>${native.maven.plugin.version}</version>
|
<version>${native.maven.plugin.version}</version>
|
||||||
<!-- 启用插件扩展,允许在 build 生命周期中无须额外配置 -->
|
|
||||||
<extensions>true</extensions>
|
<extensions>true</extensions>
|
||||||
|
|
||||||
|
<configuration>
|
||||||
|
<mainClass>org.jcnc.snow.cli.SnowCLI</mainClass>
|
||||||
|
<imageName>Snow</imageName>
|
||||||
|
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||||
|
<buildArgs>
|
||||||
|
<buildArg>--static</buildArg>
|
||||||
|
<buildArg>--libc=musl</buildArg>
|
||||||
|
<buildArg>--emit=build-report</buildArg>
|
||||||
|
<buildArg>-O2</buildArg>
|
||||||
|
<buildArg>-H:Class=org.jcnc.snow.cli.SnowCLI</buildArg>
|
||||||
|
<buildArg>-H:CCompilerPath=/opt/musl/bin/musl-gcc</buildArg>
|
||||||
|
<buildArg>-H:CLibraryPath=/opt/musl/lib</buildArg>
|
||||||
|
</buildArgs>
|
||||||
|
</configuration>
|
||||||
|
|
||||||
<executions>
|
<executions>
|
||||||
<!-- 打包阶段生成原生可执行文件 -->
|
|
||||||
<execution>
|
<execution>
|
||||||
<id>build-native</id>
|
<id>build-native</id>
|
||||||
<goals>
|
<goals>
|
||||||
<!-- compile-no-fork 在当前 JVM 进程中执行 native-image -->
|
|
||||||
<goal>compile-no-fork</goal>
|
<goal>compile-no-fork</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<phase>package</phase>
|
<phase>package</phase>
|
||||||
</execution>
|
</execution>
|
||||||
<!-- 测试阶段运行原生镜像的测试 -->
|
|
||||||
<execution>
|
<execution>
|
||||||
<id>test-native</id>
|
<id>test-native</id>
|
||||||
<goals>
|
<goals>
|
||||||
@ -109,24 +119,6 @@
|
|||||||
<phase>test</phase>
|
<phase>test</phase>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
<configuration>
|
|
||||||
<buildArgs>
|
|
||||||
<!-- 静态链接 -->
|
|
||||||
<buildArg>--static</buildArg>
|
|
||||||
<!-- 指定 musl libc -->
|
|
||||||
<buildArg>--libc=musl</buildArg>
|
|
||||||
<!-- 输出构建报告 -->
|
|
||||||
<buildArg>--emit build-report</buildArg>
|
|
||||||
<!-- 优化级别 O2 -->
|
|
||||||
<buildArg>-O2</buildArg>
|
|
||||||
</buildArgs>
|
|
||||||
<environment>
|
|
||||||
<!-- 指定使用 musl 工具链 -->
|
|
||||||
<PATH>/opt/musl-1.2.5/bin:${env.PATH}</PATH>
|
|
||||||
<C_INCLUDE_PATH>/opt/musl-1.2.5/include</C_INCLUDE_PATH>
|
|
||||||
<LIBRARY_PATH>/opt/musl-1.2.5/lib</LIBRARY_PATH>
|
|
||||||
</environment>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user