name: Next Build and Release
on:
push:
tags:
- 'v*'
- '[0-9][0-9]*'
workflow_dispatch:
inputs:
release:
description: 'Create release (push images, create GitHub Release)'
required: false
default: true
type: boolean
platforms:
description: 'Platforms to build (comma separated: linux,windows,macos)'
required: false
default: 'linux,windows,macos' type: string
build_docker:
description: 'Build Docker image'
required: false
default: true type: boolean
docker_registries:
description: 'Docker registries to push to (comma separated: ghcr.io,docker.io)'
required: false
default: 'ghcr.io,docker.io' type: string
use_china_mirror:
description: 'Use China mirror for faster downloads'
required: false
default: false type: boolean
optimize_binary:
description: 'Optimize binaries with strip/UPX (reduce file size)'
required: false
default: true
type: boolean
optimization_level:
description: 'UPX compression level (1-9, higher = smaller but slower)'
required: false
default: '9'
type: string
env:
BINARY_NAME: ${{ github.event.repository.name }}
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
CONFIG_PLATFORMS: ${{ vars.PLATFORMS || 'linux,windows,macos' }}
CONFIG_BUILD_DOCKER: ${{ vars.BUILD_DOCKER || 'true' }}
CONFIG_DOCKER_REGISTRIES: ${{ vars.DOCKER_REGISTRIES || 'ghcr.io,docker.io' }}
CONFIG_USE_CHINA_MIRROR: ${{ vars.USE_CHINA_MIRROR || 'false' }}
CONFIG_OPTIMIZE_BINARY: ${{ vars.OPTIMIZE_BINARY || 'true' }}
CONFIG_OPTIMIZATION_LEVEL: ${{ vars.OPTIMIZATION_LEVEL || '9' }}
jobs:
setup-platforms:
name: Setup Platform Flags
runs-on: ubuntu-latest
outputs:
build_linux: ${{ steps.parse-platforms.outputs.build_linux }}
build_windows: ${{ steps.parse-platforms.outputs.build_windows }}
build_macos: ${{ steps.parse-platforms.outputs.build_macos }}
build_docker: ${{ steps.get-config.outputs.build_docker }}
version: ${{ steps.get-version.outputs.version }}
has_binaries: ${{ steps.parse-platforms.outputs.has_binaries }}
push_ghcr: ${{ steps.parse-registries.outputs.push_ghcr }}
push_dockerhub: ${{ steps.parse-registries.outputs.push_dockerhub }}
any_registry: ${{ steps.parse-registries.outputs.any_registry }}
has_dockerhub_creds: ${{ steps.check-dockerhub-creds.outputs.has_dockerhub_creds }}
repository_lower: ${{ steps.get-repo-lower.outputs.repository_lower }}
binary_name_lower: ${{ steps.get-repo-lower.outputs.binary_name_lower }}
should_release: ${{ steps.check-trigger.outputs.should_release }}
use_china_mirror: ${{ steps.get-config.outputs.use_china_mirror }}
optimize_binary: ${{ steps.get-config.outputs.optimize_binary }}
optimization_level: ${{ steps.get-config.outputs.optimization_level }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check trigger type
id: check-trigger
run: |
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref_type }}" == "tag" ]]; then
echo "should_release=true" >> $GITHUB_OUTPUT
echo "🚀 Tag push detected: ${{ github.ref_name }}"
else
# 手动触发时,使用输入参数的值
echo "should_release=${{ inputs.release }}" >> $GITHUB_OUTPUT
echo "🔧 Manual workflow dispatch"
fi
- name: Get repository name in lowercase
id: get-repo-lower
run: |
REPO_LOWER=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
BINARY_LOWER=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]')
echo "repository_lower=$REPO_LOWER" >> $GITHUB_OUTPUT
echo "binary_name_lower=$BINARY_LOWER" >> $GITHUB_OUTPUT
- name: Get configuration
id: get-config
run: |
# 手动触发时,优先使用输入参数
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
PLATFORMS="${{ inputs.platforms }}"
BUILD_DOCKER="${{ inputs.build_docker }}"
DOCKER_REGISTRIES="${{ inputs.docker_registries }}"
USE_CHINA_MIRROR="${{ inputs.use_china_mirror }}"
OPTIMIZE_BINARY="${{ inputs.optimize_binary }}"
OPTIMIZATION_LEVEL="${{ inputs.optimization_level }}"
else
# Tag 触发时,使用环境变量
PLATFORMS="$CONFIG_PLATFORMS"
BUILD_DOCKER="$CONFIG_BUILD_DOCKER"
DOCKER_REGISTRIES="$CONFIG_DOCKER_REGISTRIES"
USE_CHINA_MIRROR="$CONFIG_USE_CHINA_MIRROR"
OPTIMIZE_BINARY="$CONFIG_OPTIMIZE_BINARY"
OPTIMIZATION_LEVEL="$CONFIG_OPTIMIZATION_LEVEL"
fi
echo "platforms=$PLATFORMS" >> $GITHUB_OUTPUT
echo "build_docker=$BUILD_DOCKER" >> $GITHUB_OUTPUT
echo "docker_registries=$DOCKER_REGISTRIES" >> $GITHUB_OUTPUT
echo "use_china_mirror=$USE_CHINA_MIRROR" >> $GITHUB_OUTPUT
echo "optimize_binary=$OPTIMIZE_BINARY" >> $GITHUB_OUTPUT
echo "optimization_level=$OPTIMIZATION_LEVEL" >> $GITHUB_OUTPUT
echo "⚙️ Configuration:"
echo " Platforms: $PLATFORMS"
echo " Build Docker: $BUILD_DOCKER"
echo " Docker Registries: $DOCKER_REGISTRIES"
echo " China Mirror: $USE_CHINA_MIRROR"
echo " Optimize Binaries: $OPTIMIZE_BINARY (level: $OPTIMIZATION_LEVEL)"
- name: Parse platforms
id: parse-platforms
env:
CONFIG_PLATFORMS: ${{ steps.get-config.outputs.platforms }}
run: |
PLATFORMS=$(echo "$CONFIG_PLATFORMS" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
if [[ "$PLATFORMS" == *"linux"* ]]; then
echo "build_linux=true" >> $GITHUB_OUTPUT
else
echo "build_linux=false" >> $GITHUB_OUTPUT
fi
if [[ "$PLATFORMS" == *"windows"* ]]; then
echo "build_windows=true" >> $GITHUB_OUTPUT
else
echo "build_windows=false" >> $GITHUB_OUTPUT
fi
if [[ "$PLATFORMS" == *"macos"* ]]; then
echo "build_macos=true" >> $GITHUB_OUTPUT
else
echo "build_macos=false" >> $GITHUB_OUTPUT
fi
# 检查是否有任何二进制构建
if [[ "$PLATFORMS" == *"linux"* ]] || \
[[ "$PLATFORMS" == *"windows"* ]] || \
[[ "$PLATFORMS" == *"macos"* ]]; then
echo "has_binaries=true" >> $GITHUB_OUTPUT
else
echo "has_binaries=false" >> $GITHUB_OUTPUT
fi
- name: Parse docker registries
id: parse-registries
env:
CONFIG_DOCKER_REGISTRIES: ${{ steps.get-config.outputs.docker_registries }}
run: |
REGISTRIES=$(echo "$CONFIG_DOCKER_REGISTRIES" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
if [[ "$REGISTRIES" == *"ghcr.io"* ]] || [[ "$REGISTRIES" == *"ghcr"* ]]; then
echo "push_ghcr=true" >> $GITHUB_OUTPUT
else
echo "push_ghcr=false" >> $GITHUB_OUTPUT
fi
if [[ "$REGISTRIES" == *"docker.io"* ]] || [[ "$REGISTRIES" == *"dockerhub"* ]] || [[ "$REGISTRIES" == *"hub.docker.com"* ]]; then
echo "push_dockerhub=true" >> $GITHUB_OUTPUT
else
echo "push_dockerhub=false" >> $GITHUB_OUTPUT
fi
if [[ "$REGISTRIES" == *"ghcr"* ]] || [[ "$REGISTRIES" == *"docker"* ]] || [[ -n "$REGISTRIES" ]]; then
echo "any_registry=true" >> $GITHUB_OUTPUT
else
echo "any_registry=false" >> $GITHUB_OUTPUT
fi
- name: Check Docker Hub credentials
id: check-dockerhub-creds
run: |
if [[ -n "$DOCKERHUB_USERNAME" ]] && [[ -n "$DOCKERHUB_TOKEN" ]]; then
echo "has_dockerhub_creds=true" >> $GITHUB_OUTPUT
else
echo "has_dockerhub_creds=false" >> $GITHUB_OUTPUT
fi
- name: Get version from tag
id: get-version
run: |
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
VERSION="${GITHUB_REF#refs/tags/v}"
elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
VERSION="${GITHUB_REF#refs/tags/}"
else
VERSION="latest"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Debug all outputs
run: |
echo "=== DEBUG OUTPUTS ==="
echo "build_linux: ${{ steps.parse-platforms.outputs.build_linux }}"
echo "build_windows: ${{ steps.parse-platforms.outputs.build_windows }}"
echo "build_macos: ${{ steps.parse-platforms.outputs.build_macos }}"
echo "has_binaries: ${{ steps.parse-platforms.outputs.has_binaries }}"
echo "build_docker config: ${{ steps.get-config.outputs.build_docker }}"
echo "any_registry: ${{ steps.parse-registries.outputs.any_registry }}"
echo "push_ghcr: ${{ steps.parse-registries.outputs.push_ghcr }}"
echo "push_dockerhub: ${{ steps.parse-registries.outputs.push_dockerhub }}"
echo "has_dockerhub_creds: ${{ steps.check-dockerhub-creds.outputs.has_dockerhub_creds }}"
echo "should_release: ${{ steps.check-trigger.outputs.should_release }}"
build-linux:
name: Build Linux Binary
runs-on: ubuntu-latest
needs: setup-platforms
if: ${{ needs.setup-platforms.outputs.build_linux == 'true' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install optimization tools
run: |
sudo apt-get update
sudo apt-get install -y musl-tools upx-ucl binutils
echo "📦 Installed: musl-tools, upx, binutils"
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
target: x86_64-unknown-linux-musl
- name: Build static binary
run: |
# 构建优化的二进制文件
cargo build --release --target x86_64-unknown-linux-musl
echo "📊 Original binary size:"
ls -lh target/x86_64-unknown-linux-musl/release/$BINARY_NAME
- name: Optimize binary
id: optimize-linux
if: ${{ needs.setup-platforms.outputs.optimize_binary == 'true' }}
run: |
INPUT_BINARY="target/x86_64-unknown-linux-musl/release/$BINARY_NAME"
OUTPUT_DIR="linux-release"
RELEASE_FILE="$BINARY_NAME-linux-amd64"
mkdir -p $OUTPUT_DIR
# 1. 首先复制原始文件
cp "$INPUT_BINARY" "$OUTPUT_DIR/$RELEASE_FILE-original"
ORIGINAL_SIZE=$(stat -c%s "$OUTPUT_DIR/$RELEASE_FILE-original")
# 2. 使用strip移除调试符号
echo "🔪 Stripping debug symbols..."
cp "$INPUT_BINARY" "$OUTPUT_DIR/$RELEASE_FILE-stripped"
strip --strip-all "$OUTPUT_DIR/$RELEASE_FILE-stripped"
STRIPPED_SIZE=$(stat -c%s "$OUTPUT_DIR/$RELEASE_FILE-stripped")
# 3. 使用UPX压缩
echo "📦 Compressing with UPX..."
cp "$OUTPUT_DIR/$RELEASE_FILE-stripped" "$OUTPUT_DIR/$RELEASE_FILE"
upx --best --lzma "$OUTPUT_DIR/$RELEASE_FILE"
UPX_SIZE=$(stat -c%s "$OUTPUT_DIR/$RELEASE_FILE")
# 设置最终文件权限
chmod +x "$OUTPUT_DIR/$RELEASE_FILE"
# 计算压缩率
ORIGINAL_MB=$(echo "scale=2; $ORIGINAL_SIZE / 1024 / 1024" | bc)
UPX_MB=$(echo "scale=2; $UPX_SIZE / 1024 / 1024" | bc)
REDUCTION=$(echo "scale=2; (1 - $UPX_SIZE / $ORIGINAL_SIZE) * 100" | bc)
echo "📊 Size comparison:" >> $GITHUB_STEP_SUMMARY
echo "- Original: $ORIGINAL_MB MB ($ORIGINAL_SIZE bytes)" >> $GITHUB_STEP_SUMMARY
echo "- Stripped: $(echo "scale=2; $STRIPPED_SIZE / 1024 / 1024" | bc) MB ($STRIPPED_SIZE bytes)" >> $GITHUB_STEP_SUMMARY
echo "- UPX compressed: $UPX_MB MB ($UPX_SIZE bytes)" >> $GITHUB_STEP_SUMMARY
echo "- **Reduction: $REDUCTION%**" >> $GITHUB_STEP_SUMMARY
echo "original_size=$ORIGINAL_SIZE" >> $GITHUB_OUTPUT
echo "optimized_size=$UPX_SIZE" >> $GITHUB_OUTPUT
echo "reduction=$REDUCTION" >> $GITHUB_OUTPUT
- name: Prepare unoptimized binary (fallback)
if: ${{ needs.setup-platforms.outputs.optimize_binary != 'true' }}
run: |
mkdir -p linux-release
RELEASE_FILE="$BINARY_NAME-linux-amd64"
cp target/x86_64-unknown-linux-musl/release/$BINARY_NAME "linux-release/$RELEASE_FILE"
chmod +x "linux-release/$RELEASE_FILE"
ORIGINAL_SIZE=$(stat -c%s "linux-release/$RELEASE_FILE")
ORIGINAL_MB=$(echo "scale=2; $ORIGINAL_SIZE / 1024 / 1024" | bc)
echo "📊 Binary size: $ORIGINAL_MB MB ($ORIGINAL_SIZE bytes)" >> $GITHUB_STEP_SUMMARY
- name: Test optimized binary
run: |
RELEASE_FILE="$BINARY_NAME-linux-amd64"
echo "🧪 Testing binary..."
./linux-release/$RELEASE_FILE --version
# 检查文件信息
echo "🔍 File info:"
file ./linux-release/$RELEASE_FILE
if [[ "${{ needs.setup-platforms.outputs.optimize_binary }}" == "true" ]]; then
echo "🔍 UPX info:"
upx -t ./linux-release/$RELEASE_FILE || true
fi
- name: Generate checksums
run: |
cd linux-release
for file in $BINARY_NAME-*; do
if [[ -f "$file" ]]; then
echo "🔏 Generating checksums for: $file"
sha256sum "$file" > "$file.sha256"
sha512sum "$file" > "$file.sha512"
fi
done
echo "📋 Generated files:"
ls -lh
- name: Upload Linux release
uses: actions/upload-artifact@v4
with:
name: linux-release
path: linux-release/
build-windows:
name: Build Windows Binary
runs-on: windows-latest
needs: setup-platforms
if: ${{ needs.setup-platforms.outputs.build_windows == 'true' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
target: x86_64-pc-windows-msvc
- name: Build
shell: pwsh
run: |
cargo build --release --target x86_64-pc-windows-msvc
Write-Host "📊 Original binary size:"
$exePath = "target\x86_64-pc-windows-msvc\release\$env:BINARY_NAME.exe"
if (Test-Path $exePath) {
$file = Get-Item $exePath
Write-Host " File: $($file.Name)"
Write-Host " Size: $([math]::Round($file.Length/1MB, 2)) MB"
Write-Host " Path: $exePath"
} else {
Write-Host " ❌ File not found: $exePath"
}
- name: Install UPX for Windows
if: ${{ needs.setup-platforms.outputs.optimize_binary == 'true' }}
shell: pwsh
run: |
# 下载并安装 UPX
$upxUrl = "https://github.com/upx/upx/releases/download/v4.2.1/upx-4.2.1-win64.zip"
$upxZip = "upx.zip"
$upxDir = "upx"
Write-Host "📦 Downloading UPX..."
Invoke-WebRequest -Uri $upxUrl -OutFile $upxZip
Expand-Archive -Path $upxZip -DestinationPath $upxDir
# 添加到 PATH
$upxPath = (Resolve-Path "$upxDir/upx-*/").Path
Add-Content $env:GITHUB_PATH "$upxPath"
Write-Host "UPX installed to: $upxPath"
- name: Optimize Windows binary
if: ${{ needs.setup-platforms.outputs.optimize_binary == 'true' }}
shell: pwsh
run: |
$artifactDir = "windows-release"
New-Item -ItemType Directory -Force -Path $artifactDir
$inputExe = "target\x86_64-pc-windows-msvc\release\$env:BINARY_NAME.exe"
$releaseFile = "$env:BINARY_NAME-windows-amd64.exe"
# 1. 复制原始文件
$originalPath = "$artifactDir\$releaseFile.original"
Copy-Item $inputExe $originalPath
$originalSize = (Get-Item $originalPath).Length
# 2. 剥离调试信息(Windows使用strip的方式不同)
Write-Host "🔪 Processing binary..."
$strippedPath = "$artifactDir\$releaseFile.stripped"
Copy-Item $inputExe $strippedPath
# 3. 使用 UPX 压缩
Write-Host "📦 Compressing with UPX..."
$finalPath = "$artifactDir\$releaseFile"
Copy-Item $strippedPath $finalPath
# 使用 UPX 压缩,指定压缩级别
$level = "${{ needs.setup-platforms.outputs.optimization_level }}"
upx --best --lzma "-$level" $finalPath
$upxSize = (Get-Item $finalPath).Length
# 计算压缩率
$originalMB = [math]::Round($originalSize / 1MB, 2)
$upxMB = [math]::Round($upxSize / 1MB, 2)
$reduction = [math]::Round((1 - $upxSize / $originalSize) * 100, 2)
Write-Host "📊 Size comparison:"
Write-Host "- Original: ${originalMB}MB ($originalSize bytes)"
Write-Host "- UPX compressed: ${upxMB}MB ($upxSize bytes)"
Write-Host "- **Reduction: ${reduction}%**"
# 写入摘要
Add-Content -Path $env:GITHUB_STEP_SUMMARY "## Windows Binary Optimization"
Add-Content -Path $env:GITHUB_STEP_SUMMARY "- Original: ${originalMB}MB ($originalSize bytes)"
Add-Content -Path $env:GITHUB_STEP_SUMMARY "- UPX compressed: ${upxMB}MB ($upxSize bytes)"
Add-Content -Path $env:GITHUB_STEP_SUMMARY "- **Reduction: ${reduction}%**"
- name: Prepare unoptimized Windows binary
if: ${{ needs.setup-platforms.outputs.optimize_binary != 'true' }}
shell: pwsh
run: |
$artifactDir = "windows-release"
New-Item -ItemType Directory -Force -Path $artifactDir
$releaseFile = "$env:BINARY_NAME-windows-amd64.exe"
$finalPath = "$artifactDir\$releaseFile"
Copy-Item "target\x86_64-pc-windows-msvc\release\$env:BINARY_NAME.exe" $finalPath
$originalSize = (Get-Item $finalPath).Length
$originalMB = [math]::Round($originalSize / 1MB, 2)
Write-Host "📊 Binary size: ${originalMB}MB ($originalSize bytes)"
Add-Content -Path $env:GITHUB_STEP_SUMMARY "## Windows Binary"
Add-Content -Path $env:GITHUB_STEP_SUMMARY "- Size: ${originalMB}MB ($originalSize bytes)"
- name: Test Windows binary
shell: pwsh
run: |
$releaseFile = "$env:BINARY_NAME-windows-amd64.exe"
Write-Host "🧪 Testing binary..."
& .\windows-release\$releaseFile --version
Write-Host "🔍 File info:"
Get-Item .\windows-release\$releaseFile | Format-List *
- name: Generate checksums for Windows
shell: pwsh
run: |
cd windows-release
Get-ChildItem -File | Where-Object { $_.Name -like "$env:BINARY_NAME-*" -and $_.Extension -notmatch "\.(sha256|sha512)" } | ForEach-Object {
$file = $_.Name
Write-Host "🔏 Generating checksums for: $file"
# SHA256
$hash256 = Get-FileHash $file -Algorithm SHA256
"$($hash256.Hash.ToLower()) $file" | Out-File -FilePath "$file.sha256" -Encoding UTF8
# SHA512
$hash512 = Get-FileHash $file -Algorithm SHA512
"$($hash512.Hash.ToLower()) $file" | Out-File -FilePath "$file.sha512" -Encoding UTF8
}
Write-Host "📋 Generated files:"
Get-ChildItem | Format-Table Name, @{Name="Size(MB)";Expression={[math]::Round($_.Length/1MB,2)}} -AutoSize
- name: Upload Windows release
uses: actions/upload-artifact@v4
with:
name: windows-release
path: windows-release/
build-macos:
name: Build macOS Binaries
runs-on: macos-latest
needs: setup-platforms
if: ${{ needs.setup-platforms.outputs.build_macos == 'true' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install optimization tools
run: |
brew install upx binutils
echo "📦 Installed: upx, binutils"
- name: Prepare build directory
run: |
mkdir -p macos-release
- name: Build and optimize x86_64
if: ${{ !contains(needs.setup-platforms.outputs.platforms, 'arm64') || contains(needs.setup-platforms.outputs.platforms, 'x86_64') }}
run: |
echo "🔨 Building for x86_64..."
rustup target add x86_64-apple-darwin
cargo build --release --target x86_64-apple-darwin
RELEASE_FILE="$BINARY_NAME-macos-x86_64"
INPUT_BINARY="target/x86_64-apple-darwin/release/$BINARY_NAME"
# 记录原始大小
ORIGINAL_SIZE=$(stat -f%z "$INPUT_BINARY")
ORIGINAL_MB=$(echo "scale=2; $ORIGINAL_SIZE / 1024 / 1024" | bc)
echo "Original size: $ORIGINAL_MB MB"
if [[ "${{ needs.setup-platforms.outputs.optimize_binary }}" == "true" ]]; then
# 1. 使用strip
echo "🔪 Stripping debug symbols..."
cp "$INPUT_BINARY" "macos-release/$RELEASE_FILE"
strip -x "macos-release/$RELEASE_FILE"
STRIPPED_SIZE=$(stat -f%z "macos-release/$RELEASE_FILE")
# 2. 使用UPX压缩
echo "📦 Compressing with UPX..."
upx --best --lzma --force-macos "macos-release/$RELEASE_FILE"
UPX_SIZE=$(stat -f%z "macos-release/$RELEASE_FILE")
# 计算压缩率
UPX_MB=$(echo "scale=2; $UPX_SIZE / 1024 / 1024" | bc)
REDUCTION=$(echo "scale=2; (1 - $UPX_SIZE / $ORIGINAL_SIZE) * 100" | bc)
echo "📊 Size comparison:" >> $GITHUB_STEP_SUMMARY
echo "- x86_64 Original: $ORIGINAL_MB MB" >> $GITHUB_STEP_SUMMARY
echo "- x86_64 Stripped: $(echo "scale=2; $STRIPPED_SIZE / 1024 / 1024" | bc) MB" >> $GITHUB_STEP_SUMMARY
echo "- x86_64 UPX: $UPX_MB MB" >> $GITHUB_STEP_SUMMARY
echo "- x86_64 Reduction: $REDUCTION%" >> $GITHUB_STEP_SUMMARY
else
# 未优化的版本
cp "$INPUT_BINARY" "macos-release/$RELEASE_FILE"
chmod +x "macos-release/$RELEASE_FILE"
echo "- x86_64 (unoptimized): $ORIGINAL_MB MB" >> $GITHUB_STEP_SUMMARY
fi
- name: Build and optimize aarch64
if: ${{ !contains(needs.setup-platforms.outputs.platforms, 'x86_64') || contains(needs.setup-platforms.outputs.platforms, 'arm64') }}
run: |
echo "🔨 Building for aarch64..."
rustup target add aarch64-apple-darwin
cargo build --release --target aarch64-apple-darwin
RELEASE_FILE="$BINARY_NAME-macos-arm64"
INPUT_BINARY="target/aarch64-apple-darwin/release/$BINARY_NAME"
# 记录原始大小
ORIGINAL_SIZE=$(stat -f%z "$INPUT_BINARY")
ORIGINAL_MB=$(echo "scale=2; $ORIGINAL_SIZE / 1024 / 1024" | bc)
echo "Original size: $ORIGINAL_MB MB"
if [[ "${{ needs.setup-platforms.outputs.optimize_binary }}" == "true" ]]; then
# 1. 使用strip
echo "🔪 Stripping debug symbols..."
cp "$INPUT_BINARY" "macos-release/$RELEASE_FILE"
strip -x "macos-release/$RELEASE_FILE"
STRIPPED_SIZE=$(stat -f%z "macos-release/$RELEASE_FILE")
# 2. 使用UPX压缩
echo "📦 Compressing with UPX..."
upx --best --lzma --force-macos "macos-release/$RELEASE_FILE"
UPX_SIZE=$(stat -f%z "macos-release/$RELEASE_FILE")
# 计算压缩率
UPX_MB=$(echo "scale=2; $UPX_SIZE / 1024 / 1024" | bc)
REDUCTION=$(echo "scale=2; (1 - $UPX_SIZE / $ORIGINAL_SIZE) * 100" | bc)
echo "- arm64 Original: $ORIGINAL_MB MB" >> $GITHUB_STEP_SUMMARY
echo "- arm64 Stripped: $(echo "scale=2; $STRIPPED_SIZE / 1024 / 1024" | bc) MB" >> $GITHUB_STEP_SUMMARY
echo "- arm64 UPX: $UPX_MB MB" >> $GITHUB_STEP_SUMMARY
echo "- arm64 Reduction: $REDUCTION%" >> $GITHUB_STEP_SUMMARY
else
# 未优化的版本
cp "$INPUT_BINARY" "macos-release/$RELEASE_FILE"
chmod +x "macos-release/$RELEASE_FILE"
echo "- arm64 (unoptimized): $ORIGINAL_MB MB" >> $GITHUB_STEP_SUMMARY
fi
- name: Create universal binary (可选)
if: ${{ contains(needs.setup-platforms.outputs.platforms, 'x86_64') && contains(needs.setup-platforms.outputs.platforms, 'arm64') }}
run: |
echo "🔧 Creating universal binary..."
if [[ -f "target/x86_64-apple-darwin/release/$BINARY_NAME" ]] && [[ -f "target/aarch64-apple-darwin/release/$BINARY_NAME" ]]; then
lipo -create \
"target/x86_64-apple-darwin/release/$BINARY_NAME" \
"target/aarch64-apple-darwin/release/$BINARY_NAME" \
-output "macos-release/$BINARY_NAME-macos-universal"
if [[ "${{ needs.setup-platforms.outputs.optimize_binary }}" == "true" ]]; then
# 优化通用二进制文件
echo "🔪 Stripping universal binary..."
strip -x "macos-release/$BINARY_NAME-macos-universal"
echo "📦 Compressing universal binary with UPX..."
upx --best --lzma "macos-release/$BINARY_NAME-macos-universal"
else
chmod +x "macos-release/$BINARY_NAME-macos-universal"
fi
UNIVERSAL_SIZE=$(stat -f%z "macos-release/$BINARY_NAME-macos-universal")
UNIVERSAL_MB=$(echo "scale=2; $UNIVERSAL_SIZE / 1024 / 1024" | bc)
echo "- Universal binary: $UNIVERSAL_MB MB" >> $GITHUB_STEP_SUMMARY
fi
- name: Test binaries
run: |
echo "🧪 Testing built binaries..."
for binary in macos-release/$BINARY_NAME-macos-*; do
if [[ -f "$binary" ]] && [[ ! "$binary" =~ \.(sha256|sha512)$ ]]; then
echo "Testing: $(basename $binary)"
./$binary --version || true
echo "🔍 File info for $(basename $binary):"
file "$binary"
if [[ "${{ needs.setup-platforms.outputs.optimize_binary }}" == "true" ]]; then
upx -t "$binary" 2>/dev/null || true
fi
fi
done
- name: Generate checksums for macOS
run: |
cd macos-release
for file in $BINARY_NAME-*; do
if [[ -f "$file" ]] && [[ ! "$file" =~ \.(sha256|sha512)$ ]]; then
echo "🔏 Generating checksums for: $file"
sha256sum "$file" > "$file.sha256"
sha512sum "$file" > "$file.sha512"
fi
done
echo "📋 Generated files:"
ls -lh
- name: Upload macOS release
uses: actions/upload-artifact@v4
with:
name: macos-release
path: macos-release/
create-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs:
- setup-platforms
- build-linux
- build-windows
- build-macos
permissions:
contents: write
if: >
needs.setup-platforms.outputs.should_release == 'true' &&
needs.setup-platforms.outputs.has_binaries == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download all release artifacts
run: |
gh run download ${{ github.run_id }} --dir release-files
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare final release
run: |
mkdir -p final-release
# 收集所有发布的文件
find release-files -type f \( \
-name "$BINARY_NAME-*" \
-o -name "*.sha256" \
-o -name "*.sha512" \
\) -exec cp {} final-release/ \;
# 设置可执行权限
for file in final-release/$BINARY_NAME-*; do
if [[ -f "$file" ]] && [[ ! "$file" =~ \.(sha256|sha512)$ ]]; then
chmod +x "$file"
fi
done
echo "📦 Release files:"
ls -lh final-release/
# 显示优化信息
echo ""
echo "📊 Optimization Summary:"
echo "========================"
if [[ "${{ needs.setup-platforms.outputs.optimize_binary }}" == "true" ]]; then
echo "✅ Binaries optimized with:"
echo " - strip: Removed debug symbols"
echo " - UPX: Binary compression (level ${{ needs.setup-platforms.outputs.optimization_level }})"
echo " - Expected size reduction: 40-70%"
else
echo "ℹ️ Binaries NOT optimized (raw release builds)"
fi
- name: Verify checksums
run: |
cd final-release
echo "🔐 Verifying checksums..."
# 验证所有SHA256文件
for sha_file in *.sha256; do
if [[ -f "$sha_file" ]]; then
binary="${sha_file%.sha256}"
if [[ -f "$binary" ]]; then
echo "Verifying: $binary"
if sha256sum -c "$sha_file"; then
echo " ✅ Checksum OK"
else
echo " ❌ Checksum FAILED"
exit 1
fi
fi
fi
done
echo "✅ All checksums verified successfully"
- name: Create release notes with optimization info
run: |
VERSION="${{ needs.setup-platforms.outputs.version }}"
OPTIMIZED="${{ needs.setup-platforms.outputs.optimize_binary }}"
OPT_LEVEL="${{ needs.setup-platforms.outputs.optimization_level }}"
cat > release-notes.md << EOF
# Release v$VERSION
## Build Information
- **Version**: $VERSION
- **Optimization**: $([[ "$OPTIMIZED" == "true" ]] && echo "✅ Enabled (UPX level $OPT_LEVEL + strip)" || echo "❌ Disabled")
- **Build Date**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
## Downloads
| Platform | Architecture | File | Size | SHA256 |
|----------|-------------|------|------|--------|
EOF
cd final-release
for file in $BINARY_NAME-*; do
if [[ -f "$file" ]] && [[ ! "$file" =~ \.(sha256|sha512)$ ]]; then
size=$(ls -lh "$file" | awk '{print $5}')
if [[ "$file" =~ linux ]]; then
platform="Linux"
elif [[ "$file" =~ windows ]]; then
platform="Windows"
elif [[ "$file" =~ macos ]]; then
platform="macOS"
else
platform="Other"
fi
if [[ "$file" =~ x86_64|amd64 ]]; then
arch="x86_64"
elif [[ "$file" =~ arm64 ]]; then
arch="ARM64"
elif [[ "$file" =~ universal ]]; then
arch="Universal"
elif [[ "$file" =~ aarch64 ]]; then
arch="ARM64"
else
arch="Unknown"
fi
sha256_file="$file.sha256"
sha256=""
if [[ -f "$sha256_file" ]]; then
sha256=$(head -c 16 "$sha256_file" | cut -d' ' -f1)...
fi
echo "| $platform | $arch | \`$file\` | $size | \`$sha256\` |" >> ../release-notes.md
fi
done
cd ..
cat >> release-notes.md << EOF
## Verification
To verify the integrity of downloaded files:
\`\`\`bash
# Example for Linux binary
sha256sum -c $BINARY_NAME-linux-amd64.sha256
\`\`\`
## Optimization Details
EOF
if [[ "$OPTIMIZED" == "true" ]]; then
cat >> release-notes.md << EOF
Binaries have been optimized for size:
1. **strip**: Removed debug symbols and unnecessary metadata
2. **UPX**: Compressed the binary executable (compression level: $OPT_LEVEL)
**Note**: UPX-compressed binaries are self-extracting and may have a slightly slower startup time.
EOF
else
cat >> release-notes.md << EOF
Binaries are unoptimized release builds. They include debug symbols and are larger in size.
EOF
fi
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: final-release/*
body_path: release-notes.md
generate_release_notes: false
draft: false
prerelease: ${{ contains(needs.setup-platforms.outputs.version, '-alpha') || contains(needs.setup-platforms.outputs.version, '-beta') || contains(needs.setup-platforms.outputs.version, '-rc') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-docker:
name: Build Docker Image
runs-on: ubuntu-latest
needs: setup-platforms
if: needs.setup-platforms.outputs.build_docker == 'true'
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to container registries
if: needs.setup-platforms.outputs.should_release == 'true'
run: |
if [ "${{ needs.setup-platforms.outputs.push_ghcr }}" = "true" ]; then
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
fi
if [ "${{ needs.setup-platforms.outputs.push_dockerhub }}" = "true" ] && \
[ "${{ needs.setup-platforms.outputs.has_dockerhub_creds }}" = "true" ]; then
echo "${{ env.DOCKERHUB_TOKEN }}" | docker login docker.io -u ${{ env.DOCKERHUB_USERNAME }} --password-stdin
fi
- name: Prepare Docker tags
id: prepare-tags
run: |
TAGS=""
VERSION="${{ needs.setup-platforms.outputs.version }}"
if [ "${{ needs.setup-platforms.outputs.push_ghcr }}" = "true" ]; then
TAGS="$TAGS,ghcr.io/${{ needs.setup-platforms.outputs.repository_lower }}:$VERSION"
TAGS="$TAGS,ghcr.io/${{ needs.setup-platforms.outputs.repository_lower }}:latest"
fi
if [ "${{ needs.setup-platforms.outputs.push_dockerhub }}" = "true" ] && \
[ "${{ needs.setup-platforms.outputs.has_dockerhub_creds }}" = "true" ]; then
TAGS="$TAGS,$DOCKERHUB_USERNAME/${{ needs.setup-platforms.outputs.binary_name_lower }}:$VERSION"
TAGS="$TAGS,$DOCKERHUB_USERNAME/${{ needs.setup-platforms.outputs.binary_name_lower }}:latest"
fi
echo "tags=${TAGS#,}" >> $GITHUB_OUTPUT
- name: Build and push Docker image
if: steps.prepare-tags.outputs.tags
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
push: ${{ needs.setup-platforms.outputs.should_release == 'true' }}
tags: ${{ steps.prepare-tags.outputs.tags }}
build-args: |
USE_CHINA_MIRROR=${{ needs.setup-platforms.outputs.use_china_mirror }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-report:
name: Build and Release Report
runs-on: ubuntu-latest
needs:
- setup-platforms
- build-linux
- build-windows
- build-macos
- create-release
- build-docker
if: always()
steps:
- name: Generate comprehensive report
run: |
echo "# 🎉 Build and Release Report"
echo ""
echo "## 📋 Summary"
echo ""
echo "**Version**: ${{ needs.setup-platforms.outputs.version }}"
echo "**Release Created**: ${{ needs.setup-platforms.outputs.should_release && '✅ Yes' || '❌ No' }}"
echo "**Binary Optimization**: ${{ needs.setup-platforms.outputs.optimize_binary == 'true' && '✅ Enabled' || '❌ Disabled' }}"
if [[ "${{ needs.setup-platforms.outputs.optimize_binary }}" == "true" ]]; then
echo "**Optimization Level**: ${{ needs.setup-platforms.outputs.optimization_level }}/9"
fi
echo ""
echo "## 🖥️ Platforms Built"
echo ""
echo "- Linux: ${{ needs.setup-platforms.outputs.build_linux && '✅' || '❌' }}"
echo "- Windows: ${{ needs.setup-platforms.outputs.build_windows && '✅' || '❌' }}"
echo "- macOS: ${{ needs.setup-platforms.outputs.build_macos && '✅' || '❌' }}"
echo ""
echo "## 🐳 Docker"
echo ""
echo "- Build: ${{ needs.setup-platforms.outputs.build_docker == 'true' && '✅' || '❌' }}"
echo "- GHCR: ${{ needs.setup-platforms.outputs.push_ghcr == 'true' && '✅' || '❌' }}"
echo "- Docker Hub: ${{ needs.setup-platforms.outputs.push_dockerhub == 'true' && needs.setup-platforms.outputs.has_dockerhub_creds == 'true' && '✅' || '❌' }}"
if [[ "${{ needs.setup-platforms.outputs.optimize_binary }}" == "true" ]]; then
echo ""
echo "## 📦 Optimization Details"
echo ""
echo "Binaries were optimized using:"
echo "1. **strip**: Removes debug symbols and unnecessary metadata"
echo "2. **UPX**: Executable compressor (Ultimate Packer for eXecutables)"
echo "3. **Level**: ${{ needs.setup-platforms.outputs.optimization_level }}/9 (higher = smaller but slower compression)"
echo ""
echo "**Expected benefits**:"
echo "- 40-70% size reduction"
echo "- Faster downloads"
echo "- Self-extracting at runtime"
fi
echo ""
echo "## 📊 Artifacts"
echo ""
echo "All binaries include:"
echo "- SHA256 checksum file"
echo "- SHA512 checksum file"
echo "- Executable permissions set"