name: Build Windows Release with MSYS2 on: push: branches: - main tags: - 'v*' pull_request: branches: - main workflow_dispatch: jobs: build-windows-msys2: # 需要 Gitea act_runner 运行在 Windows,并且能访问 GitHub 以拉取 actions/checkout、msys2/setup-msys2、actions/upload-artifact。 # 如果你的 runner 使用了自定义标签,可以在 Gitea runner 配置中给它同时加上 windows 标签。 runs-on: - windows defaults: run: shell: msys2 {0} steps: - name: Checkout uses: actions/checkout@v4 - name: Setup MSYS2 build environment uses: msys2/setup-msys2@v2 with: msystem: UCRT64 update: true install: >- git curl zip p7zip mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja mingw-w64-ucrt-x86_64-qt6-base mingw-w64-ucrt-x86_64-opencv - name: Configure run: | set -euo pipefail cmake -S . -B build-windows-msys2 -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH=/ucrt64 - name: Build run: | set -euo pipefail cmake --build build-windows-msys2 --parallel "$(nproc)" - name: Package run: | set -euo pipefail APP_NAME="chengnan" PACKAGE_NAME="chengnan-windows-x86_64-msys2" PACKAGE_DIR="dist-windows-msys2/${PACKAGE_NAME}" EXE="build-windows-msys2/${APP_NAME}.exe" test -f "$EXE" rm -rf dist-windows-msys2 mkdir -p "$PACKAGE_DIR" cp "$EXE" "$PACKAGE_DIR/" # 部署 Qt 运行时和插件。 if command -v windeployqt >/dev/null 2>&1; then windeployqt --release --no-translations "$PACKAGE_DIR/${APP_NAME}.exe" elif command -v windeployqt6 >/dev/null 2>&1; then windeployqt6 --release --no-translations "$PACKAGE_DIR/${APP_NAME}.exe" else echo "windeployqt not found" >&2 exit 1 fi # 收集 MinGW/MSYS2/OpenCV 运行时 DLL。ldd 会列出 exe/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 < <(ldd "$binary" \ | awk '/=> \/ucrt64\/bin\// {print $3} /^\/ucrt64\/bin\// {print $1}' \ | sort -u) done < <(find "$PACKAGE_DIR" -maxdepth 2 -type f \( -name '*.exe' -o -name '*.dll' \) | sort) done cat > "$PACKAGE_DIR/README.txt" <<'EOF' chengnan Windows x86_64 MSYS2 发布包 运行方式: 1. 解压整个目录。 2. 双击 chengnan.exe 运行。 说明: - 本目录由 Windows Runner 使用 MSYS2 UCRT64 构建生成。 - 本目录已包含 Qt、OpenCV 和 MinGW 运行时 DLL。 - 请不要只复制 chengnan.exe,需保留同目录 DLL 和 Qt plugins 子目录。 EOF (cd dist-windows-msys2 && 7z a -tzip "${PACKAGE_NAME}.zip" "${PACKAGE_NAME}" >/dev/null) (cd dist-windows-msys2 && sha256sum "${PACKAGE_NAME}.zip" > "${PACKAGE_NAME}.zip.sha256") # 当前 Gitea 上传入口约 60 秒会返回 502;大文件按 15 MiB 分片上传。 ZIP="dist-windows-msys2/${PACKAGE_NAME}.zip" split -b 15M -d -a 3 "$ZIP" "${ZIP}.part-" (cd dist-windows-msys2 && 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-msys2/chengnan-windows-x86_64-msys2.zip test -s dist-windows-msys2/chengnan-windows-x86_64-msys2.zip.part-000 test -s dist-windows-msys2/chengnan-windows-x86_64-msys2.zip.sha256 test -s dist-windows-msys2/chengnan-windows-x86_64-msys2.zip.parts.sha256 (cd dist-windows-msys2 && sha256sum -c chengnan-windows-x86_64-msys2.zip.sha256) (cd dist-windows-msys2 && sha256sum -c chengnan-windows-x86_64-msys2.zip.parts.sha256) 7z l dist-windows-msys2/chengnan-windows-x86_64-msys2.zip | grep 'chengnan.exe' limit=$((20 * 1024 * 1024)) for part in dist-windows-msys2/chengnan-windows-x86_64-msys2.zip.part-*; do size="$(wc -c < "$part")" echo "$(basename "$part") $size" if [ "$size" -gt "$limit" ]; then echo "$part is too large for the current Gitea upload path" >&2 exit 1 fi done - name: Upload artifact uses: actions/upload-artifact@v3 with: name: chengnan-windows-x86_64-msys2 path: | dist-windows-msys2/chengnan-windows-x86_64-msys2.zip dist-windows-msys2/chengnan-windows-x86_64-msys2.zip.sha256 dist-windows-msys2/chengnan-windows-x86_64-msys2.zip.part-* dist-windows-msys2/chengnan-windows-x86_64-msys2.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-msys2" 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-msys2/${PACKAGE_NAME}.zip.part-*; do upload_file "$part" "${API_BASE}/$(basename "$part")" done upload_file "dist-windows-msys2/${PACKAGE_NAME}.zip.sha256" "${API_BASE}/${PACKAGE_NAME}.zip.sha256" upload_file "dist-windows-msys2/${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"