diff --git a/.gitignore b/.gitignore index a547bf3..a96fee1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ lerna-debug.log* node_modules dist dist-ssr +releases *.local # Editor directories and files diff --git a/README.md b/README.md index 4ea1db3..00811b8 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,46 @@ pnpm tauri build # Tauri Android pnpm tauri android build + +# Pacman 包(Arch Linux,需先执行 pnpm tauri build) +./scripts/pacman-pkg.sh +``` + +### 4. 收集发布产物 + +将各平台构建产物统一收集到 `releases//`,并规范命名: + +```bash +# Linux / macOS +./scripts/collect-artifacts.sh + +# Windows +scripts\collect-artifacts.cmd +``` + +脚本会**跳过不存在的构建产物**,因此可以放心运行——你当前平台没构建的产物会被忽略。覆盖范围: + +| 平台 | 产物 | +|------|------| +| Web | `.tar.gz` `.zip` | +| Linux | `.AppImage` `.deb` `.rpm` `.pkg.tar.zst` | +| Windows | `.msi` `.exe` | +| macOS | `.dmg` | +| Android | `.apk`(arm64 / x86_64 / universal) | + +输出示例: + +``` +releases/0.1.0/ +├── bookmgr-client-0.1.0-android-arm64.apk +├── bookmgr-client-0.1.0-android-universal.apk +├── bookmgr-client-0.1.0-android-x86_64.apk +├── bookmgr-client-0.1.0-linux-amd64.AppImage +├── bookmgr-client-0.1.0-linux-amd64.deb +├── bookmgr-client-0.1.0-linux-x86_64.pkg.tar.zst +├── bookmgr-client-0.1.0-linux-x86_64.rpm +├── bookmgr-client-0.1.0-web.tar.gz +└── bookmgr-client-0.1.0-web.zip ``` ## 项目结构 diff --git a/scripts/collect-artifacts.cmd b/scripts/collect-artifacts.cmd new file mode 100644 index 0000000..cc470d6 --- /dev/null +++ b/scripts/collect-artifacts.cmd @@ -0,0 +1,144 @@ +@echo off +setlocal enabledelayedexpansion + +set "SCRIPT_DIR=%~dp0" +set "ROOT=%SCRIPT_DIR%.." + +:: --------------------------------------------------------------- +:: 读取版本号 +:: --------------------------------------------------------------- +for /f "usebackq delims=" %%i in (`powershell -Command "try { (Get-Content '%ROOT%\src-tauri\tauri.conf.json' -Raw | ConvertFrom-Json).version } catch { Write-Output '0.0.0' }"`) do set "VERSION=%%i" +if "%VERSION%"=="0.0.0" ( + echo 错误:无法读取 tauri.conf.json 中的版本号 + exit /b 1 +) + +set "OUT_DIR=%ROOT%\releases\%VERSION%" +set "PKG=bookmgr-client" +set "DIST=%ROOT%\dist" +set "BUNDLE=%ROOT%\src-tauri\target\release\bundle" + +echo ==^> 收集 %PKG% v%VERSION% 产物 → %OUT_DIR% + +:: --------------------------------------------------------------- +:: 准备输出目录 +:: --------------------------------------------------------------- +if exist "%OUT_DIR%" rmdir /s /q "%OUT_DIR%" +mkdir "%OUT_DIR%" + +:: --------------------------------------------------------------- +:: Web — 从 dist/ 打 tar.gz 和 zip +:: --------------------------------------------------------------- +if exist "%DIST%\index.html" ( + set "TARBALL=%OUT_DIR%\%PKG%-%VERSION%-web.tar.gz" + echo [web] !TARBALL! + tar -czf "!TARBALL!" -C "%DIST%" . + + set "ZIPBALL=%OUT_DIR%\%PKG%-%VERSION%-web.zip" + echo [web] !ZIPBALL! + powershell -Command "Compress-Archive -Path '%DIST%\*' -DestinationPath '!ZIPBALL!' -Force" +) else ( + echo [web] 跳过(dist/ 未就绪,请先运行 pnpm build) +) + +:: --------------------------------------------------------------- +:: Windows — msi / nsis +:: --------------------------------------------------------------- +if exist "%BUNDLE%\msi" ( + for %%f in ("%BUNDLE%\msi\%PKG%_*.msi") do ( + if exist "%%f" ( + set "DST=%OUT_DIR%\%PKG%-%VERSION%-windows-x86_64.msi" + echo [msi] !DST! + copy /y "%%f" "!DST!" >nul + ) + ) +) + +if exist "%BUNDLE%\nsis" ( + for %%f in ("%BUNDLE%\nsis\%PKG%_*.exe") do ( + if exist "%%f" ( + set "DST=%OUT_DIR%\%PKG%-%VERSION%-windows-x86_64-setup.exe" + echo [nsis] !DST! + copy /y "%%f" "!DST!" >nul + ) + ) +) + +:: --------------------------------------------------------------- +:: Linux — AppImage / deb / rpm / pacman +:: --------------------------------------------------------------- +if exist "%BUNDLE%\appimage" ( + for %%f in ("%BUNDLE%\appimage\%PKG%_*_amd64.AppImage") do ( + if exist "%%f" ( + set "DST=%OUT_DIR%\%PKG%-%VERSION%-linux-amd64.AppImage" + echo [appimage] !DST! + copy /y "%%f" "!DST!" >nul + ) + ) +) + +if exist "%BUNDLE%\deb" ( + for %%f in ("%BUNDLE%\deb\%PKG%_*_amd64.deb") do ( + if exist "%%f" ( + set "DST=%OUT_DIR%\%PKG%-%VERSION%-linux-amd64.deb" + echo [deb] !DST! + copy /y "%%f" "!DST!" >nul + ) + ) +) + +if exist "%BUNDLE%\rpm" ( + for %%f in ("%BUNDLE%\rpm\%PKG%-*x86_64.rpm") do ( + if exist "%%f" ( + set "DST=%OUT_DIR%\%PKG%-%VERSION%-linux-x86_64.rpm" + echo [rpm] !DST! + copy /y "%%f" "!DST!" >nul + ) + ) +) + +if exist "%BUNDLE%\pacman" ( + for %%f in ("%BUNDLE%\pacman\%PKG%-*.pkg.tar.zst") do ( + if exist "%%f" ( + set "fn=%%~nxf" + :: 替换 pkgrel 为 linux 标识 + set "newname=!fn:-1-x86_64=-linux-x86_64!" + set "DST=%OUT_DIR%\!newname!" + echo [pacman] !DST! + copy /y "%%f" "!DST!" >nul + ) + ) +) + +:: --------------------------------------------------------------- +:: Android — APK +:: --------------------------------------------------------------- +set "APK_DIR=%ROOT%\src-tauri\gen\android\app\build\outputs\apk" +if exist "%APK_DIR%" ( + for %%a in (arm64 x86_64 universal) do ( + set "SRC=%APK_DIR%\%%a\release\app-%%a-release.apk" + if exist "!SRC!" ( + set "DST=%OUT_DIR%\%PKG%-%VERSION%-android-%%a.apk" + echo [apk] !DST! + copy /y "!SRC!" "!DST!" >nul + ) + ) +) + +:: --------------------------------------------------------------- +:: macOS — dmg +:: --------------------------------------------------------------- +if exist "%BUNDLE%\dmg" ( + for %%f in ("%BUNDLE%\dmg\%PKG%_*.dmg") do ( + if exist "%%f" ( + set "DST=%OUT_DIR%\%PKG%-%VERSION%-macos-arm64.dmg" + echo [dmg] !DST! + copy /y "%%f" "!DST!" >nul + ) + ) +) + +:: --------------------------------------------------------------- +echo. +echo ==^> 完成 +dir "%OUT_DIR%" diff --git a/scripts/collect-artifacts.sh b/scripts/collect-artifacts.sh new file mode 100755 index 0000000..42c41b3 --- /dev/null +++ b/scripts/collect-artifacts.sh @@ -0,0 +1,140 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(dirname "$SCRIPT_DIR")" + +VERSION=$(jq -r '.version' "$ROOT/src-tauri/tauri.conf.json") +OUT_DIR="$ROOT/releases/$VERSION" +PKG="bookmgr-client" + +# --------------------------------------------------------------- +# 准备输出目录 +# --------------------------------------------------------------- +rm -rf "$OUT_DIR" +mkdir -p "$OUT_DIR" + +echo "==> 收集 $PKG v$VERSION 产物 → $OUT_DIR" + +# --------------------------------------------------------------- +# Web — 从 dist/ 打 tar.gz 和 zip +# --------------------------------------------------------------- +DIST="$ROOT/dist" +if [[ -d "$DIST" ]] && [[ -f "$DIST/index.html" ]]; then + TARBALL="$OUT_DIR/${PKG}-${VERSION}-web.tar.gz" + echo " [web] $TARBALL" + tar -czf "$TARBALL" -C "$DIST" . + + ZIPBALL="$OUT_DIR/${PKG}-${VERSION}-web.zip" + echo " [web] $ZIPBALL" + (cd "$DIST" && zip -qr "$ZIPBALL" .) +else + echo " [web] 跳过(dist/ 未就绪,请先运行 pnpm build)" +fi + +# --------------------------------------------------------------- +# Linux — AppImage / deb / rpm / pacman +# --------------------------------------------------------------- +BUNDLE="$ROOT/src-tauri/target/release/bundle" + +if [[ -d "$BUNDLE/appimage" ]]; then + SRC=$(echo "$BUNDLE/appimage/${PKG}_"*_amd64.AppImage) + if [[ -f "$SRC" ]]; then + DST="$OUT_DIR/${PKG}-${VERSION}-linux-amd64.AppImage" + echo " [appimage] $DST" + cp "$SRC" "$DST" + fi +fi + +if [[ -d "$BUNDLE/deb" ]]; then + SRC=$(echo "$BUNDLE/deb/${PKG}_"*_amd64.deb) + if [[ -f "$SRC" ]]; then + DST="$OUT_DIR/${PKG}-${VERSION}-linux-amd64.deb" + echo " [deb] $DST" + cp "$SRC" "$DST" + fi +fi + +if [[ -d "$BUNDLE/rpm" ]]; then + SRC=$(echo "$BUNDLE/rpm/${PKG}-"*x86_64.rpm) + if [[ -f "$SRC" ]]; then + DST="$OUT_DIR/${PKG}-${VERSION}-linux-x86_64.rpm" + echo " [rpm] $DST" + cp "$SRC" "$DST" + fi +fi + +if [[ -d "$BUNDLE/pacman" ]]; then + for f in "$BUNDLE/pacman/${PKG}-"*.pkg.tar.zst; do + [[ -f "$f" ]] || continue + base=$(basename "$f") + # bookmgr-client-0.1.0-1-x86_64.pkg.tar.zst → bookmgr-client-0.1.0-linux-x86_64.pkg.tar.zst + newname=$(echo "$base" | sed -E 's/-1-(x86_64|aarch64)/-linux-\1/') + DST="$OUT_DIR/$newname" + echo " [pacman] $DST" + cp "$f" "$DST" + done +fi + +# --------------------------------------------------------------- +# Android — APK +# --------------------------------------------------------------- +APK_DIR="$ROOT/src-tauri/gen/android/app/build/outputs/apk" +if [[ -d "$APK_DIR" ]]; then + for arch in arm64 x86_64 universal; do + SRC="$APK_DIR/$arch/release/app-${arch}-release.apk" + if [[ -f "$SRC" ]]; then + DST="$OUT_DIR/${PKG}-${VERSION}-android-${arch}.apk" + echo " [apk] $DST" + cp "$SRC" "$DST" + fi + done +fi + +# --------------------------------------------------------------- +# Windows — msi / nsis +# --------------------------------------------------------------- +if [[ -d "$BUNDLE/msi" ]]; then + for f in "$BUNDLE/msi/${PKG}_"*.msi; do + [[ -f "$f" ]] || continue + base=$(basename "$f") + # bookmgr-client_0.1.0_x64_en-US.msi → bookmgr-client-0.1.0-windows-x86_64.msi + newname=$(echo "$base" | sed -E 's/_/ /g; s/ /-/g; s/x64/x86_64/; s/en-US-//') + newname="${PKG}-${VERSION}-windows-${newname#${PKG}-${VERSION}-}" + DST="$OUT_DIR/$newname" + echo " [msi] $DST" + cp "$f" "$DST" + done +fi + +if [[ -d "$BUNDLE/nsis" ]]; then + for f in "$BUNDLE/nsis/${PKG}_"*.exe; do + [[ -f "$f" ]] || continue + base=$(basename "$f") + newname=$(echo "$base" | sed -E 's/_/ /g; s/ /-/g; s/x64/x86_64/; s/en-US-//') + newname="${PKG}-${VERSION}-windows-${newname#${PKG}-${VERSION}-}" + DST="$OUT_DIR/$newname" + echo " [nsis] $DST" + cp "$f" "$DST" + done +fi + +# --------------------------------------------------------------- +# macOS — dmg +# --------------------------------------------------------------- +if [[ -d "$BUNDLE/dmg" ]]; then + for f in "$BUNDLE/dmg/${PKG}_"*.dmg; do + [[ -f "$f" ]] || continue + base=$(basename "$f") + newname=$(echo "$base" | sed -E 's/_/ /g; s/ /-/g; s/x64/x86_64/; s/aarch64/arm64/') + newname="${PKG}-${VERSION}-macos-${newname#${PKG}-${VERSION}-}" + DST="$OUT_DIR/$newname" + echo " [dmg] $DST" + cp "$f" "$DST" + done +fi + +# --------------------------------------------------------------- +echo "" +echo "==> 完成,共 $(find "$OUT_DIR" -maxdepth 1 -type f | wc -l) 个文件:" +ls -lh "$OUT_DIR" diff --git a/scripts/pacman-pkg.sh b/scripts/pacman-pkg.sh new file mode 100755 index 0000000..665a15d --- /dev/null +++ b/scripts/pacman-pkg.sh @@ -0,0 +1,74 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(dirname "$SCRIPT_DIR")" + +# --------------------------------------------------------------- +# 1. 检查二进制 +# --------------------------------------------------------------- +BIN="$ROOT/src-tauri/target/release/bookmgr-client" +if [[ ! -f "$BIN" ]]; then + echo "错误:未找到 $BIN,请先运行 pnpm tauri build" + exit 1 +fi + +# --------------------------------------------------------------- +# 2. 读取版本号 +# --------------------------------------------------------------- +VERSION=$(jq -r '.version' "$ROOT/src-tauri/tauri.conf.json") +PKG="bookmgr-client" +PKGVER="${VERSION}-1" +ARCH="x86_64" +OUTPUT="${PKG}-${PKGVER}-${ARCH}.pkg.tar.zst" + +DESKTOP_FILE="$ROOT/src-tauri/target/release/bundle/deb/${PKG}_${VERSION}_amd64/data/usr/share/applications/${PKG}.desktop" +ICON_FILE="$ROOT/src-tauri/icons/128x128.png" + +echo "==> 打包 ${PKG} ${PKGVER} (${ARCH})" + +# --------------------------------------------------------------- +# 3. 构建 PKGBUILD + 安装目录 +# --------------------------------------------------------------- +BUILD_DIR=$(mktemp -d) +trap 'rm -rf "$BUILD_DIR"' EXIT + +SRCDIR="$BUILD_DIR/src" +mkdir -p "$SRCDIR/usr/bin" +mkdir -p "$SRCDIR/usr/share/applications" +mkdir -p "$SRCDIR/usr/share/icons/hicolor/128x128/apps" + +install -Dm755 "$BIN" "$SRCDIR/usr/bin/${PKG}" +install -Dm644 "$DESKTOP_FILE" "$SRCDIR/usr/share/applications/${PKG}.desktop" +install -Dm644 "$ICON_FILE" "$SRCDIR/usr/share/icons/hicolor/128x128/apps/${PKG}.png" + +sed -i 's/^Categories=$/Categories=Utility;/' "$SRCDIR/usr/share/applications/${PKG}.desktop" + +cat > "$BUILD_DIR/PKGBUILD" << PKGEOF +pkgname=${PKG} +pkgver=${VERSION} +pkgrel=1 +pkgdesc="图书管理系统桌面客户端" +arch=(x86_64) +url="https://git.msksbr.com/msksbr/bookMgr-client" +license=(MIT) +depends=(gtk3 webkit2gtk-4.1 libappindicator-gtk3) +package() { + cp -r "\$srcdir/"* "\$pkgdir/" +} +PKGEOF + +# --------------------------------------------------------------- +# 4. 用 makepkg 生成 .pkg.tar.zst +# --------------------------------------------------------------- +BUNDLE_DIR="$ROOT/src-tauri/target/release/bundle/pacman" +rm -rf "$BUNDLE_DIR" +mkdir -p "$BUNDLE_DIR" + +cd "$BUILD_DIR" +makepkg -R + +echo "" +echo "==> 输出: $(ls ./*.pkg.tar.zst)" +cp ./*.pkg.tar.zst "$BUNDLE_DIR/" +echo "==> 已复制到 $BUNDLE_DIR"