Skip to content

Build All UI APP

Build All UI APP #13

Workflow file for this run

name: Build All UI APP
on:
workflow_dispatch:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
jobs:
prepare:
name: Prepare Version and Create Release
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.VERSION }}
app_version: ${{ steps.version.outputs.APP_VERSION }}
release_tag: ${{ steps.version.outputs.RELEASE_TAG }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Get version info
id: version
run: |
BASE_VERSION=$(cat VERSION)
# 使用当前日期时间生成tag (格式: v20260202-HHMMSS)
CURRENT_DATE=$(TZ='Asia/Shanghai' date +'%Y%m%d-%H%M%S')
RELEASE_TAG="v${CURRENT_DATE}"
APP_VERSION="${BASE_VERSION}"
echo "VERSION=${RELEASE_TAG}" >> $GITHUB_OUTPUT
echo "APP_VERSION=${APP_VERSION}" >> $GITHUB_OUTPUT
echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_OUTPUT
echo "Building version: ${RELEASE_TAG}"
echo "App version: ${APP_VERSION}"
- name: Create Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
RELEASE_TAG="${{ steps.version.outputs.RELEASE_TAG }}"
# 检查release是否已存在
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
echo "Release $RELEASE_TAG already exists, deleting it..."
gh release delete "$RELEASE_TAG" -y || true
git push --delete origin "$RELEASE_TAG" || true
fi
# 创建新的release
echo "Creating release $RELEASE_TAG..."
gh release create "$RELEASE_TAG" \
--title "Release $RELEASE_TAG" \
--notes "Automated build for $RELEASE_TAG" \
--draft=false \
--prerelease=false
echo "Release $RELEASE_TAG created successfully"
build-android:
name: Build Android APK
needs: prepare
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
cache: true
- name: Install Fyne CLI
run: |
go install fyne.io/tools/cmd/fyne@latest
- name: Set up JDK 17
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
- name: Install Android SDK
uses: android-actions/setup-android@v4
with:
packages: 'platform-tools'
- name: Install Android NDK
run: |
sdkmanager --install "ndk;25.2.9519653"
echo "ANDROID_NDK_HOME=$ANDROID_SDK_ROOT/ndk/25.2.9519653" >> $GITHUB_ENV
- name: Install Android Build Tools
run: |
sdkmanager --install "build-tools;34.0.0"
sdkmanager --install "platforms;android-34"
- name: Configure Git for Private Modules
run: |
git config --global url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/"
env:
GITHUB_TOKEN: ${{ secrets.GHT }}
- name: Download dependencies
run: go mod download
env:
GOPRIVATE: github.com/oneclickvirt/security,github.com/oneclickvirt/privatespeedtest
- name: Verify dependencies
run: go mod verify
- name: Update FyneApp.toml version
run: |
sed -i "s/Version = .*/Version = \"${{ needs.prepare.outputs.version }}\"/" FyneApp.toml
cat FyneApp.toml
- name: Build Android APK (arm64)
env:
ANDROID_NDK_HOME: ${{ env.ANDROID_NDK_HOME }}
GOPRIVATE: github.com/oneclickvirt/security,github.com/oneclickvirt/privatespeedtest
FYNE_BUILD_FLAGS: "-trimpath -buildvcs=false -ldflags '-s -w -buildid='"
run: |
fyne package --os android --app-id com.oneclickvirt.ecsgui --app-version "${{ needs.prepare.outputs.app_version }}" --release
if [ -f *.apk ]; then
mv *.apk ecs-gui-android-arm64-${{ needs.prepare.outputs.version }}.apk
echo "ARM64 APK 构建成功"
ls -lh ecs-gui-android-arm64-${{ needs.prepare.outputs.version }}.apk
else
echo "ARM64 APK 构建失败"
exit 1
fi
- name: Build Android APK (x86_64)
env:
ANDROID_NDK_HOME: ${{ env.ANDROID_NDK_HOME }}
GOPRIVATE: github.com/oneclickvirt/security,github.com/oneclickvirt/privatespeedtest
FYNE_BUILD_FLAGS: "-trimpath -buildvcs=false -ldflags '-s -w -buildid='"
run: |
fyne package --os android/amd64 --app-id com.oneclickvirt.ecsgui --app-version "${{ needs.prepare.outputs.app_version }}" --release
# 查找新生成的 APK(排除已重命名的 arm64 版本)
NEW_APK=$(find . -maxdepth 1 -name "*.apk" -not -name "ecs-gui-android-*" -type f | head -n 1)
if [ -n "$NEW_APK" ]; then
mv "$NEW_APK" ecs-gui-android-x86_64-${{ needs.prepare.outputs.version }}.apk
echo "x86_64 APK 构建成功"
ls -lh ecs-gui-android-x86_64-${{ needs.prepare.outputs.version }}.apk
else
echo "x86_64 APK 构建失败"
exit 1
fi
- name: Install UPX
run: |
wget -q https://github.com/upx/upx/releases/download/v4.2.1/upx-4.2.1-amd64_linux.tar.xz
tar -xf upx-4.2.1-amd64_linux.tar.xz
sudo mv upx-4.2.1-amd64_linux/upx /usr/local/bin/
upx --version
- name: Compress APK with UPX
run: |
for apk in ecs-gui-android-*.apk; do
if [ -f "$apk" ]; then
echo "压缩前大小: $(du -h "$apk")"
# 解压 APK
unzip -q "$apk" -d "${apk%.apk}_extracted"
# 对 lib 目录下的 .so 文件使用 UPX 压缩
find "${apk%.apk}_extracted/lib" -name "*.so" -type f | while read so_file; do
echo "正在压缩: $so_file"
upx --best --lzma "$so_file" || echo "警告: $so_file 压缩失败,继续处理"
done
# 重新打包 APK
cd "${apk%.apk}_extracted"
zip -r -q -9 "../${apk%.apk}_compressed.apk" .
cd ..
# 替换原 APK
mv "${apk%.apk}_compressed.apk" "$apk"
rm -rf "${apk%.apk}_extracted"
echo "压缩后大小: $(du -h "$apk")"
fi
done
- name: Download signing keystore
run: |
# 使用 GitHub Token 下载私有仓库的 JKS 文件
curl -L \
-H "Authorization: token ${{ secrets.GHT }}" \
-H "Accept: application/vnd.github.v3.raw" \
-o oneclickvirt.jks \
"https://api.github.com/repos/oneclickvirt/jks/contents/oneclickvirt.jks"
# 验证文件是否下载成功
if [ -f oneclickvirt.jks ]; then
echo "✓ JKS 文件下载成功"
ls -lh oneclickvirt.jks
else
echo "✗ JKS 文件下载失败"
exit 1
fi
- name: Sign APK files
run: |
# 安装 apksigner (包含在 Android build-tools 中)
BUILD_TOOLS_VERSION="34.0.0"
APKSIGNER="$ANDROID_SDK_ROOT/build-tools/$BUILD_TOOLS_VERSION/apksigner"
if [ ! -f "$APKSIGNER" ]; then
echo "✗ apksigner 未找到"
exit 1
fi
echo "使用 apksigner: $APKSIGNER"
# 对所有 APK 文件进行签名
for apk in ecs-gui-android-*.apk; do
if [ -f "$apk" ]; then
echo "正在签名: $apk"
SIGNED_APK="${apk%.apk}_signed.apk"
# 签名 APK
$APKSIGNER sign \
--ks oneclickvirt.jks \
--ks-key-alias gui \
--ks-pass pass:${{ secrets.KEYSTORE_PASSWORD }} \
--key-pass pass:${{ secrets.KEY_PASSWORD }} \
--out "$SIGNED_APK" \
"$apk"
# 验证签名
$APKSIGNER verify "$SIGNED_APK"
if [ $? -eq 0 ]; then
echo "✓ $apk 签名成功"
# 替换原始 APK
mv "$SIGNED_APK" "$apk"
else
echo "✗ $apk 签名验证失败"
exit 1
fi
fi
done
# 清理 JKS 文件
rm -f oneclickvirt.jks
- name: List build artifacts
run: |
ls -lh ecs-gui-android-*.apk
du -sh ecs-gui-android-*.apk
- name: Upload to release
shell: bash
run: |
RELEASE_TAG="${{ needs.prepare.outputs.release_tag }}"
for file in ecs-gui-android-*.apk; do
if [ -f "$file" ]; then
echo "Uploading $file to release $RELEASE_TAG"
gh release upload "$RELEASE_TAG" "$file" --clobber
fi
done
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload ARM64 APK
uses: actions/upload-artifact@v7
with:
name: ecs-gui-android-arm64-${{ needs.prepare.outputs.version }}
path: ecs-gui-android-arm64-${{ needs.prepare.outputs.version }}.apk
retention-days: 90
- name: Upload x86_64 APK
uses: actions/upload-artifact@v7
with:
name: ecs-gui-android-x86_64-${{ needs.prepare.outputs.version }}
path: ecs-gui-android-x86_64-${{ needs.prepare.outputs.version }}.apk
retention-days: 90
build-desktop:
name: Build Desktop Apps
needs: prepare
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# macOS builds
- os: macos-latest
platform: darwin
arch: arm64
name: macos-arm64
- os: macos-15-intel
platform: darwin
arch: amd64
name: macos-amd64
# Windows builds
- os: windows-2025
platform: windows
arch: amd64
name: windows-amd64
# Linux builds
- os: ubuntu-24.04-arm
platform: linux
arch: arm64
name: linux-arm64
- os: ubuntu-latest
platform: linux
arch: amd64
name: linux-amd64
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
cache: true
- name: Install Fyne CLI
run: go install fyne.io/tools/cmd/fyne@latest
- name: Install Linux dependencies
if: matrix.platform == 'linux'
run: |
if [ "${{ matrix.arch }}" == "arm64" ]; then
sudo dpkg --add-architecture arm64
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu libc6-dev-arm64-cross
sudo apt-get install -y libgl1-mesa-dev xorg-dev
sudo apt-get install -y \
libgl1-mesa-dev:arm64 \
libx11-dev:arm64 \
libxrandr-dev:arm64 \
libxxf86vm-dev:arm64 \
libxi-dev:arm64 \
libxcursor-dev:arm64 \
libxinerama-dev:arm64
else
# AMD64 使用默认源
sudo apt-get update
sudo apt-get install -y libgl1-mesa-dev xorg-dev
fi
- name: Configure Git for Private Modules
run: |
git config --global url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/"
env:
GITHUB_TOKEN: ${{ secrets.GHT }}
- name: Download dependencies
run: go mod download
env:
GOPRIVATE: github.com/oneclickvirt/security,github.com/oneclickvirt/privatespeedtest
- name: Verify dependencies
run: go mod verify
- name: Update FyneApp.toml version
shell: bash
run: |
if [ "${{ runner.os }}" == "macOS" ]; then
sed -i '' "s/Version = .*/Version = \"${{ needs.prepare.outputs.version }}\"/" FyneApp.toml
else
sed -i "s/Version = .*/Version = \"${{ needs.prepare.outputs.version }}\"/" FyneApp.toml
fi
cat FyneApp.toml
- name: Install UPX
shell: bash
run: |
if [ "${{ runner.os }}" == "macOS" ]; then
brew install upx
upx --version
elif [ "${{ runner.os }}" == "Windows" ]; then
echo "跳过 Windows UPX 安装(不支持)"
fi
- name: Build for ${{ matrix.name }}
env:
GOPRIVATE: github.com/oneclickvirt/security,github.com/oneclickvirt/privatespeedtest
FYNE_BUILD_FLAGS: "-trimpath -buildvcs=false -ldflags '-s -w -buildid= -checklinkname=0'"
GOARCH: ${{ matrix.arch }}
shell: bash
run: |
# macOS 需要特殊处理:先编译再打包
if [ "${{ matrix.platform }}" == "darwin" ]; then
# 使用独立目录避免冲突
BUILD_DIR="build-${{ matrix.name }}"
mkdir -p "$BUILD_DIR"
echo "Building macOS binary with ldflags..."
go build -trimpath -buildvcs=false -ldflags "-checklinkname=0 -s -w -buildid=" -o "$BUILD_DIR/ecs-gui-bin" .
echo "Packaging macOS app with fyne..."
# 在根目录运行 fyne package,指定编译好的二进制文件路径
fyne package -os darwin -name ecs-gui --exe "$BUILD_DIR/ecs-gui-bin" --app-version "${{ needs.prepare.outputs.app_version }}" --release
if [ -d "ecs-gui.app" ]; then
TARFILE="ecs-gui-${{ matrix.name }}-${{ needs.prepare.outputs.version }}.tar.gz"
echo "Creating tar file: $TARFILE"
GZIP=-9 tar -czf "$TARFILE" ecs-gui.app
echo "✓ macOS app 构建成功"
# 清理构建目录和生成的 app
rm -rf "$BUILD_DIR" ecs-gui.app
else
echo "✗ macOS app 构建失败"
exit 1
fi
elif [ "${{ matrix.platform }}" == "linux" ]; then
# Linux 构建 - 使用独立目录避免冲突
BUILD_DIR="build-${{ matrix.name }}"
mkdir -p "$BUILD_DIR"
echo "Building Linux binary for ${{ matrix.arch }}..."
if [ "${{ matrix.arch }}" == "arm64" ]; then
# ARM64 交叉编译
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 \
go build -trimpath -buildvcs=false -ldflags "-checklinkname=0 -s -w -buildid=" -o "$BUILD_DIR/ecs-gui" .
else
# AMD64 本地编译
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
go build -trimpath -buildvcs=false -ldflags "-checklinkname=0 -s -w -buildid=" -o "$BUILD_DIR/ecs-gui" .
fi
echo "Packaging Linux app with fyne..."
# 在根目录运行 fyne package,指定编译好的二进制文件路径
fyne package -os linux -name ecs-gui --exe "$BUILD_DIR/ecs-gui" --app-version "${{ needs.prepare.outputs.app_version }}" --release
# 查找生成的 tar.xz 文件
if [ -f "ecs-gui.tar.xz" ]; then
mv "ecs-gui.tar.xz" ecs-gui-${{ matrix.name }}-${{ needs.prepare.outputs.version }}.tar.xz
echo "✓ Linux tar.xz 构建成功"
# 清理构建目录
rm -rf "$BUILD_DIR"
else
echo "✗ Linux tar.xz 构建失败"
ls -la .
exit 1
fi
else
# Windows 构建
echo "Building ${{ matrix.platform }} with fyne package..."
# 在根目录运行 fyne package
fyne package -os ${{ matrix.platform }} -name ecs-gui --app-version "${{ needs.prepare.outputs.app_version }}" --release
if [ "${{ matrix.platform }}" == "windows" ]; then
if [ -f "ecs-gui.exe" ]; then
mv "ecs-gui.exe" ecs-gui-${{ matrix.name }}-${{ needs.prepare.outputs.version }}.exe
echo "✓ Windows exe 构建成功"
else
echo "✗ Windows exe 构建失败"
exit 1
fi
fi
fi
- name: Compress with UPX
shell: bash
run: |
# Windows 跳过 UPX 压缩
if [ "${{ runner.os }}" == "Windows" ]; then
echo "跳过 Windows exe 的 UPX 压缩(不支持)"
exit 0
fi
for file in ecs-gui-${{ matrix.name }}-*; do
if [ -f "$file" ]; then
echo "压缩前大小: $(du -h "$file")"
if [[ "$file" == *.tar.gz ]]; then
# macOS app 需要解压后压缩二进制
echo "正在处理 macOS app: $file"
tar -xzf "$file"
# 查找并压缩 macOS 可执行文件
if [ -d ecs-gui.app/Contents/MacOS ]; then
for binary in ecs-gui.app/Contents/MacOS/*; do
if [ -f "$binary" ] && [ -x "$binary" ]; then
echo "正在压缩 macOS 二进制: $binary"
upx --best --lzma "$binary" || echo "警告: $binary 压缩失败"
fi
done
fi
# 重新打包
rm "$file"
GZIP=-9 tar -czf "$file" ecs-gui.app
rm -rf ecs-gui.app
echo "压缩后大小: $(du -h "$file")"
elif [[ "$file" == *.tar.xz ]]; then
# Linux app 需要解压后压缩二进制
echo "正在处理 Linux app: $file"
mkdir -p ecs-gui-extracted
tar -xJf "$file" -C ecs-gui-extracted
# 查找并压缩 Linux 可执行文件
find ecs-gui-extracted -type f -executable | while read binary; do
if file "$binary" | grep -q "ELF"; then
echo "正在压缩 Linux 二进制: $binary"
upx --best --lzma "$binary" || echo "警告: $binary 压缩失败"
fi
done
# 重新打包
rm "$file"
XZ_OPT=-9e tar -cJf "$file" -C ecs-gui-extracted .
rm -rf ecs-gui-extracted
echo "压缩后大小: $(du -h "$file")"
fi
fi
done
- name: List build artifacts
shell: bash
run: |
ls -lh ecs-gui-${{ matrix.name }}-*
du -sh ecs-gui-${{ matrix.name }}-*
- name: Upload to release
shell: bash
run: |
RELEASE_TAG="${{ needs.prepare.outputs.release_tag }}"
for file in ecs-gui-${{ matrix.name }}-*; do
if [ -f "$file" ]; then
echo "Uploading $file to release $RELEASE_TAG"
gh release upload "$RELEASE_TAG" "$file" --clobber
fi
done
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
name: ecs-gui-${{ matrix.name }}-${{ needs.prepare.outputs.version }}
path: ecs-gui-${{ matrix.name }}-${{ needs.prepare.outputs.version }}.*
retention-days: 90