name: Build Windows Release on: push: branches: - main tags: - 'v*' workflow_dispatch: permissions: contents: write jobs: build-windows-release: name: Build Windows x64 Release on Linux (MinGW + Qt + OpenCV) runs-on: ubuntu-latest container: fedora:latest env: BUILD_TYPE: Release PACKAGE_BASENAME: chengnan-windows-x64-mingw steps: - name: Configure Fedora TUNA mirror run: | set -euo pipefail sed -e 's|^metalink=|#metalink=|g' \ -e 's|^#baseurl=http://download.example/pub/fedora/linux|baseurl=https://mirrors.aliyun.com/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 clean all dnf makecache --refresh -y - name: Install cross build dependencies run: | set -euo pipefail dnf install -y \ git \ curl \ jq \ zip \ unzip \ python3 \ nodejs \ cmake \ ninja-build \ mingw64-gcc-c++ \ mingw64-qt6-qtbase \ mingw64-qt6-qtimageformats \ mingw64-opencv - name: Checkout uses: actions/checkout@v4 - name: Configure cross build run: | cmake -S . -B build-windows -G Ninja \ -DCMAKE_TOOLCHAIN_FILE=/usr/share/mingw/toolchain-mingw64.cmake \ -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DCMAKE_CXX_FLAGS="-isystem /usr/x86_64-w64-mingw32/sys-root/mingw/include/opencv4" \ -DOpenCV_DIR=/usr/x86_64-w64-mingw32/sys-root/mingw/lib/cmake/OpenCV - name: Build run: cmake --build build-windows --config "$BUILD_TYPE" --parallel - name: Package Windows app run: | set -euo pipefail root="/usr/x86_64-w64-mingw32/sys-root/mingw" dist="dist-windows/chengnan" rm -rf "$dist" artifacts mkdir -p "$dist/platforms" "$dist/imageformats" artifacts cp build-windows/chengnan.exe "$dist/" cp "$root/bin/Qt6Core.dll" "$dist/" cp "$root/bin/Qt6Gui.dll" "$dist/" cp "$root/bin/Qt6Widgets.dll" "$dist/" cp "$root/bin/libgcc_s_seh-1.dll" "$dist/" cp "$root/bin/libstdc++-6.dll" "$dist/" cp "$root/bin/libwinpthread-1.dll" "$dist/" cp "$root/lib/qt6/plugins/platforms/qwindows.dll" "$dist/platforms/" cp "$root/lib/qt6/plugins/imageformats/"*.dll "$dist/imageformats/" find "$root/bin" -maxdepth 1 -name "*opencv*" -exec cp -t "$dist/" {} + install -m 0644 readme.md "$dist/readme.md" cat > "$dist/README.txt" <<'EOF' chengnan Windows x64 package Usage: 1. Extract the whole directory. 2. Double-click chengnan.exe to run. Note: - This package is cross-built on Linux with MinGW + Qt + OpenCV. - Do not copy chengnan.exe alone; keep DLLs and Qt plugin directories together. EOF short_sha="${GITHUB_SHA::10}" if [ "${GITHUB_REF_TYPE:-}" = "tag" ]; then version="$GITHUB_REF_NAME" else version="build-$short_sha" fi package="$PACKAGE_BASENAME-$version.zip" (cd dist-windows && zip -r "../artifacts/$package" chengnan) sha256sum "artifacts/$package" > "artifacts/$package.sha256" printf 'VERSION=%s\n' "$version" >> "$GITHUB_ENV" printf 'PACKAGE=%s\n' "$package" >> "$GITHUB_ENV" - name: Verify package run: | set -euo pipefail test -s "artifacts/$PACKAGE" test -s "artifacts/$PACKAGE.sha256" unzip -l "artifacts/$PACKAGE" | grep -q 'chengnan/chengnan.exe' unzip -l "artifacts/$PACKAGE" | grep -q 'chengnan/platforms/qwindows.dll' unzip -l "artifacts/$PACKAGE" | grep -qE 'chengnan/.*opencv.*\.dll' sha256sum -c "artifacts/$PACKAGE.sha256" - name: Upload package to repository release env: RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | set -euo pipefail token="${RELEASE_TOKEN:-${GITHUB_TOKEN:-}}" if [ -z "$token" ]; then echo 'Missing release token. Configure RELEASE_TOKEN in repository secrets or ensure GITHUB_TOKEN is available.' >&2 exit 1 fi server_url="${GITHUB_SERVER_URL%/}" api_base="$server_url/api/v1/repos/$GITHUB_REPOSITORY" tag_name="$VERSION" if [ "${GITHUB_REF_TYPE:-}" = "tag" ]; then prerelease=false release_name="$GITHUB_REF_NAME" else prerelease=true release_name="chengnan ${GITHUB_SHA::10}" fi release_payload=$(jq -n \ --arg tag_name "$tag_name" \ --arg target_commitish "$GITHUB_SHA" \ --arg name "$release_name" \ --arg body "Automated Windows x64 MinGW build from Linux Gitea Actions." \ --argjson prerelease "$prerelease" \ '{tag_name:$tag_name,target_commitish:$target_commitish,name:$name,body:$body,draft:false,prerelease:$prerelease}') status=$(curl --silent --show-error --location \ --header "Authorization: token $token" \ --header 'Content-Type: application/json' \ --write-out '%{http_code}' \ --output release.json \ --data "$release_payload" \ "$api_base/releases") if [ "$status" != "200" ] && [ "$status" != "201" ]; then curl --silent --show-error --location \ --header "Authorization: token $token" \ --output release.json \ "$api_base/releases/tags/$tag_name" fi release_id=$(jq -r '.id' release.json) if [ -z "$release_id" ] || [ "$release_id" = "null" ]; then echo 'Failed to resolve release id.' >&2 cat release.json >&2 exit 1 fi upload_asset() { local path="$1" local name name=$(basename "$path") curl --silent --show-error --location \ --header "Authorization: token $token" \ "$api_base/releases/$release_id/assets" > assets.json jq -r --arg name "$name" '.[] | select(.name == $name) | .id' assets.json | while read -r asset_id; do [ -n "$asset_id" ] && curl --silent --show-error --location \ --request DELETE \ --header "Authorization: token $token" \ "$api_base/releases/assets/$asset_id" >/dev/null done status=$(curl --silent --show-error --location \ --header "Authorization: token $token" \ --form "attachment=@$path;filename=$name" \ --write-out '%{http_code}' \ --output upload-response.json \ "$api_base/releases/$release_id/assets?name=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1]))' "$name")") if [ "$status" != "200" ] && [ "$status" != "201" ]; then echo "Upload release asset failed, HTTP $status: $name" >&2 cat upload-response.json >&2 exit 1 fi echo "Uploaded release asset: $name" } upload_asset "artifacts/$PACKAGE" upload_asset "artifacts/$PACKAGE.sha256" echo "Release assets uploaded to $server_url/$GITHUB_REPOSITORY/releases/tag/$tag_name"