feat: add simplified Qt image viewer Dev branch

This commit is contained in:
luochen570
2026-05-30 17:18:55 +08:00
parent 4832838a0d
commit 4627343e31
21 changed files with 591 additions and 1623 deletions

View File

@@ -1,219 +0,0 @@
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_runnerrunner-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 \
python3 \
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")
du -h "dist-windows-cross/${PACKAGE_NAME}.zip"
- 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.sha256
(cd dist-windows-cross && sha256sum -c chengnan-windows-x86_64-cross.zip.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/'
- 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
if-no-files-found: error
- name: Upload package to repository release
if: ${{ github.event_name != 'pull_request' }}
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
set -euo pipefail
PACKAGE_NAME="chengnan-windows-x86_64-cross"
SERVER_URL="${GITHUB_SERVER_URL:-https://git.luochen570.cn}"
REPOSITORY="${GITHUB_REPOSITORY}"
API_BASE="${SERVER_URL}/api/v1/repos/${REPOSITORY}"
TOKEN="${RELEASE_TOKEN:-${GITHUB_TOKEN:-}}"
test -n "$TOKEN"
if [ "${GITHUB_REF_TYPE:-}" = "tag" ]; then
TAG_NAME="${GITHUB_REF_NAME}"
RELEASE_NAME="${GITHUB_REF_NAME}"
PRERELEASE=false
else
TAG_NAME="build-${GITHUB_SHA:0:10}"
RELEASE_NAME="chengnan ${GITHUB_SHA:0:10}"
PRERELEASE=true
fi
json_escape() {
python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'
}
release_payload="$(printf '{"tag_name":%s,"target_commitish":%s,"name":%s,"body":%s,"draft":false,"prerelease":%s}' \
"$(printf '%s' "$TAG_NAME" | json_escape)" \
"$(printf '%s' "${GITHUB_SHA}" | json_escape)" \
"$(printf '%s' "$RELEASE_NAME" | json_escape)" \
"$(printf 'Automated Windows x86_64 cross build from Gitea Actions.\n' | json_escape)" \
"$PRERELEASE")"
status="$(curl --show-error --silent --location \
--header "Authorization: token ${TOKEN}" \
--header 'Content-Type: application/json' \
--data "$release_payload" \
--write-out '%{http_code}' \
--output /tmp/gitea-release-response.json \
"${API_BASE}/releases")"
if [ "$status" = "409" ] || [ "$status" = "422" ]; then
RELEASE_ID="$(curl --show-error --silent --location \
--header "Authorization: token ${TOKEN}" \
"${API_BASE}/releases/tags/${TAG_NAME}" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')"
elif [ "$status" = "201" ] || [ "$status" = "200" ]; then
RELEASE_ID="$(python3 -c 'import json,sys; print(json.load(open("/tmp/gitea-release-response.json"))["id"])')"
else
echo "Create release failed with HTTP ${status}" >&2
cat /tmp/gitea-release-response.json >&2
exit 1
fi
upload_asset() {
local src="$1"
local name
local existing_id
local status
name="$(basename "$src")"
existing_id="$(curl --show-error --silent --location \
--header "Authorization: token ${TOKEN}" \
"${API_BASE}/releases/${RELEASE_ID}/assets" \
| ASSET_NAME="$name" python3 -c 'import json,os,sys; assets=json.load(sys.stdin); print(next((str(a["id"]) for a in assets if a.get("name")==os.environ["ASSET_NAME"]), ""))')"
if [ -n "$existing_id" ]; then
curl --show-error --silent --location \
--request DELETE \
--header "Authorization: token ${TOKEN}" \
--output /tmp/gitea-release-asset-delete-response.json \
"${API_BASE}/releases/${RELEASE_ID}/assets/${existing_id}"
fi
status="$(curl --show-error --silent --location \
--header "Authorization: token ${TOKEN}" \
--form "attachment=@${src};filename=${name}" \
--write-out '%{http_code}' \
--output /tmp/gitea-release-asset-response.json \
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${name}")"
if [ "$status" != "201" ] && [ "$status" != "200" ]; then
echo "Upload release asset failed with HTTP ${status}: ${name}" >&2
cat /tmp/gitea-release-asset-response.json >&2
exit 1
fi
}
upload_asset "dist-windows-cross/${PACKAGE_NAME}.zip"
upload_asset "dist-windows-cross/${PACKAGE_NAME}.zip.sha256"
echo "Release assets uploaded to ${SERVER_URL}/${REPOSITORY}/releases/tag/${TAG_NAME}"

View File

@@ -1,180 +0,0 @@
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"

View File

@@ -1,108 +0,0 @@
name: Build Windows Release
on:
workflow_dispatch:
jobs:
build-windows:
# Gitea Actions 使用自建 act_runner不提供 GitHub 托管的 windows-latest。
# 这里匹配常见的 Windows Runner 标签Runner 需注册为 windows 或 windows-latest 才会接单。
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
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: |
cmake -S . -B build-windows -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/ucrt64
- name: Build
run: |
cmake --build build-windows --parallel "$(nproc)"
- name: Package
run: |
set -euo pipefail
APP_NAME="chengnan"
PACKAGE_NAME="chengnan-windows-x86_64"
PACKAGE_DIR="dist-windows/${PACKAGE_NAME}"
EXE="build-windows/${APP_NAME}.exe"
test -f "$EXE"
rm -rf dist-windows
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 发布包
运行方式:
1. 解压整个目录。
2. 双击 chengnan.exe 运行。
说明:
- 本目录已包含 Qt、OpenCV 和 MinGW 运行时 DLL。
- 请不要只复制 chengnan.exe需保留同目录 DLL 和 Qt plugins 子目录。
EOF
(cd dist-windows && 7z a -tzip "${PACKAGE_NAME}.zip" "${PACKAGE_NAME}" >/dev/null)
(cd dist-windows && sha256sum "${PACKAGE_NAME}.zip" > "${PACKAGE_NAME}.zip.sha256")
- name: Verify package
run: |
test -s dist-windows/chengnan-windows-x86_64.zip
sha256sum -c dist-windows/chengnan-windows-x86_64.zip.sha256
7z l dist-windows/chengnan-windows-x86_64.zip | grep 'chengnan.exe'
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: chengnan-windows-x86_64
path: |
dist-windows/chengnan-windows-x86_64.zip
dist-windows/chengnan-windows-x86_64.zip.sha256
if-no-files-found: error