name: Cross Build Windows Release on Linux on: push: branches: - main tags: - 'v*' pull_request: branches: - main workflow_dispatch: jobs: cross-build-windows: # 使用可访问 GitHub 的 Gitea act_runner(runner-mihomo),可直接拉取 actions/checkout、actions/upload-artifact 等 GitHub Action。 runs-on: ubuntu-latest container: image: fedora:latest steps: - name: Prepare action runtime run: | set -euo pipefail sed -e 's|^metalink=|#metalink=|g' \ -e 's|^#baseurl=http://download.example/pub/fedora/linux|baseurl=https://mirrors.tuna.tsinghua.edu.cn/fedora|g' \ -i /etc/yum.repos.d/fedora*.repo if [ -f /etc/yum.repos.d/fedora-cisco-openh264.repo ]; then sed -i 's|^enabled=1|enabled=0|g' /etc/yum.repos.d/fedora-cisco-openh264.repo fi dnf makecache --refresh -y dnf install -y git nodejs - name: Checkout uses: actions/checkout@v4 - name: Install cross build dependencies run: | set -euo pipefail dnf install -y \ cmake \ curl \ ninja-build \ p7zip \ p7zip-plugins \ mingw64-gcc-c++ \ mingw64-qt6-qtbase \ mingw64-opencv - name: Configure run: | set -euo pipefail cmake -S . -B build-windows-cross -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_TOOLCHAIN_FILE=/usr/share/mingw/toolchain-mingw64.cmake - name: Build run: | set -euo pipefail cmake --build build-windows-cross --parallel "$(nproc)" - name: Package run: | set -euo pipefail APP_NAME="chengnan" PACKAGE_NAME="chengnan-windows-x86_64-cross" PACKAGE_DIR="dist-windows-cross/${PACKAGE_NAME}" EXE="build-windows-cross/${APP_NAME}.exe" test -f "$EXE" rm -rf dist-windows-cross mkdir -p "$PACKAGE_DIR" cp "$EXE" "$PACKAGE_DIR/" # 收集 MinGW 运行时、Qt、OpenCV 依赖 DLL。 for round in 1 2 3; do while IFS= read -r binary; do while IFS= read -r dll; do [ -f "$dll" ] || continue cp -n "$dll" "$PACKAGE_DIR/" || true done < <(x86_64-w64-mingw32-objdump -p "$binary" \ | awk '/DLL Name:/ {print $3}' \ | while read -r name; do \ find /usr/x86_64-w64-mingw32/sys-root/mingw/bin -iname "$name" -print -quit; \ done) done < <(find "$PACKAGE_DIR" -maxdepth 1 -type f \( -name '*.exe' -o -name '*.dll' \) | sort) done # Qt 平台插件是 Windows Qt GUI 程序启动所必需的。 QT_PLUGIN_ROOT="/usr/x86_64-w64-mingw32/sys-root/mingw/lib/qt6/plugins" if [ -d "$QT_PLUGIN_ROOT/platforms" ]; then mkdir -p "$PACKAGE_DIR/platforms" cp -n "$QT_PLUGIN_ROOT/platforms"/*.dll "$PACKAGE_DIR/platforms/" fi cat > "$PACKAGE_DIR/README.txt" <<'EOF' chengnan Windows x86_64 交叉编译发布包 运行方式: 1. 解压整个目录。 2. 双击 chengnan.exe 运行。 说明: - 本目录由 Linux Runner 使用 MinGW-w64 交叉编译生成。 - 请不要只复制 chengnan.exe,需保留同目录 DLL 和 platforms 子目录。 EOF (cd dist-windows-cross && 7z a -tzip "${PACKAGE_NAME}.zip" "${PACKAGE_NAME}" >/dev/null) (cd dist-windows-cross && sha256sum "${PACKAGE_NAME}.zip" > "${PACKAGE_NAME}.zip.sha256") # 当前 Gitea 上传入口约 60 秒会返回 502;大文件按 15 MiB 分片上传。 ZIP="dist-windows-cross/${PACKAGE_NAME}.zip" split -b 15M -d -a 3 "$ZIP" "${ZIP}.part-" (cd dist-windows-cross && sha256sum "${PACKAGE_NAME}.zip".part-* > "${PACKAGE_NAME}.zip.parts.sha256") du -h "$ZIP" "${ZIP}".part-* - name: Verify package run: | set -euo pipefail test -s dist-windows-cross/chengnan-windows-x86_64-cross.zip test -s dist-windows-cross/chengnan-windows-x86_64-cross.zip.part-000 test -s dist-windows-cross/chengnan-windows-x86_64-cross.zip.parts.sha256 (cd dist-windows-cross && sha256sum -c chengnan-windows-x86_64-cross.zip.sha256) (cd dist-windows-cross && sha256sum -c chengnan-windows-x86_64-cross.zip.parts.sha256) 7z l dist-windows-cross/chengnan-windows-x86_64-cross.zip | grep 'chengnan.exe' 7z l dist-windows-cross/chengnan-windows-x86_64-cross.zip | grep 'platforms/' python3 - <<'PY' from pathlib import Path limit = 20 * 1024 * 1024 for part in Path('dist-windows-cross').glob('chengnan-windows-x86_64-cross.zip.part-*'): size = part.stat().st_size print(part.name, size) if size > limit: raise SystemExit(f'{part} is too large for the current Gitea upload path') PY - name: Upload artifact uses: actions/upload-artifact@v3 with: name: chengnan-windows-x86_64-cross path: | dist-windows-cross/chengnan-windows-x86_64-cross.zip dist-windows-cross/chengnan-windows-x86_64-cross.zip.sha256 dist-windows-cross/chengnan-windows-x86_64-cross.zip.part-* dist-windows-cross/chengnan-windows-x86_64-cross.zip.parts.sha256 if-no-files-found: error - name: Upload package to Gitea Packages if: ${{ github.event_name != 'pull_request' }} env: PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }} run: | set -euo pipefail PACKAGE_NAME="chengnan-windows-x86_64-cross" VERSION="${GITHUB_SHA:-manual}" VERSION="${VERSION:0:10}" OWNER="${GITHUB_REPOSITORY%%/*}" API_BASE="${GITHUB_SERVER_URL:-https://git.luochen570.cn}/api/packages/${OWNER}/generic/chengnan/${VERSION}" TOKEN="${PACKAGE_TOKEN:-${GITHUB_TOKEN:-}}" test -n "$TOKEN" upload_file() { local src="$1" local url="$2" local status status="$(curl --show-error --silent --location \ --header "Authorization: token ${TOKEN}" \ --upload-file "$src" \ --write-out '%{http_code}' \ --output /tmp/gitea-upload-response.txt \ "$url")" if [ "$status" != "201" ] && [ "$status" != "200" ]; then echo "Upload failed with HTTP ${status}: ${url}" >&2 cat /tmp/gitea-upload-response.txt >&2 exit 1 fi } for part in dist-windows-cross/${PACKAGE_NAME}.zip.part-*; do upload_file "$part" "${API_BASE}/$(basename "$part")" done upload_file "dist-windows-cross/${PACKAGE_NAME}.zip.sha256" "${API_BASE}/${PACKAGE_NAME}.zip.sha256" upload_file "dist-windows-cross/${PACKAGE_NAME}.zip.parts.sha256" "${API_BASE}/${PACKAGE_NAME}.zip.parts.sha256" echo "Package uploaded to: ${API_BASE}/" echo "Download all ${PACKAGE_NAME}.zip.part-* files, then reconstruct with: cat ${PACKAGE_NAME}.zip.part-* > ${PACKAGE_NAME}.zip"