diff --git a/.github/workflows/linux-packages.yml b/.github/workflows/linux-packages.yml new file mode 100644 index 0000000..2bdde1c --- /dev/null +++ b/.github/workflows/linux-packages.yml @@ -0,0 +1,135 @@ +name: Linux 安装包 + +# 仓库本身在 Gitea,这个工作流是给 GitHub 镜像用的: +# 推送、打 tag(v*)、手动触发、PR 检查时编译并打出 .deb / .rpm。 +on: + push: + branches: [main, master] + tags: ["v*"] + pull_request: + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: packages-${{ github.ref }} + cancel-in-progress: true + +jobs: + package: + name: ${{ matrix.arch }} · deb + rpm + runs-on: ${{ matrix.runs-on }} + # ARM runner 只有公共仓库(或自建 arm64 runner)才有;私有仓库里这一步失败 + # 不算整体失败,x86_64 的包照常产出。 + continue-on-error: ${{ matrix.optional }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + runs-on: ubuntu-22.04 + deb_arch: amd64 + rpm_arch: x86_64 + optional: false + - arch: aarch64 + runs-on: ubuntu-24.04-arm + deb_arch: arm64 + rpm_arch: aarch64 + optional: true + + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 安装构建依赖 + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + build-essential file \ + qtbase5-dev qtbase5-dev-tools qt5-qmake \ + rpm dpkg-dev + + - name: 读取版本号 + id: meta + run: | + version=$(sed -n 's/^VERSION[[:space:]]*=[[:space:]]*//p' VoiletMark.pro | head -n1 | tr -d '[:space:]') + if [ -z "$version" ]; then + echo "VoiletMark.pro 里没有 VERSION" >&2 + exit 1 + fi + echo "version=$version" >> "$GITHUB_OUTPUT" + + - name: 编译 + run: | + qmake_bin=$(command -v qmake-qt5 || command -v qmake || true) + if [ -z "$qmake_bin" ]; then + echo "找不到 qmake(确认装了 qtbase5-dev / qt5-qmake)" >&2 + exit 1 + fi + "$qmake_bin" -v + mkdir -p build + cd build + "$qmake_bin" ../VoiletMark.pro PREFIX=/usr CONFIG+=release + make -j"$(nproc)" + make install INSTALL_ROOT="$PWD/stage" + find stage -type f | sort + + - name: 打 .deb + run: | + ./packaging/build-deb.sh build/stage "${{ steps.meta.outputs.version }}" "${{ matrix.deb_arch }}" dist + + - name: 打 .rpm + run: | + ./packaging/build-rpm.sh build/stage "${{ steps.meta.outputs.version }}" "${{ matrix.rpm_arch }}" dist + + - name: 检查产物 + run: | + ls -lh dist + dpkg-deb -I dist/*.deb + rpm -qpi dist/*.rpm + rpm -qpl dist/*.rpm + + - name: 上传构建产物 + uses: actions/upload-artifact@v4 + with: + name: voiletmark-${{ matrix.arch }} + path: dist/* + if-no-files-found: error + + release: + name: 发布到 Release + needs: package + # ARM 那个 job 可能因为拿不到 arm runner 而失败,这里用 !cancelled() 照常收尾; + # 下面单独检查 x86_64 的包在不在,缺了就不发 Release。 + if: ${{ !cancelled() && startsWith(github.ref, 'refs/tags/') }} + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - name: 下载所有产物 + uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + + - name: 确认 x86_64 的包存在 + run: | + ls -lh dist + test -n "$(ls dist/*amd64.deb 2>/dev/null)" || { + echo "没有 x86_64 的 deb,不发布" >&2 + exit 1 + } + + - name: 生成 SHA256SUMS + run: | + cd dist + sha256sum ./*.deb ./*.rpm > SHA256SUMS + cat SHA256SUMS + + - name: 挂到对应 tag 的 Release + uses: softprops/action-gh-release@v2 + with: + files: dist/* + generate_release_notes: true + fail_on_unmatched_files: true diff --git a/.gitignore b/.gitignore index dfd8062..a9800b7 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,11 @@ ui_*.h *.pro.user *.pro.user.* +# 本地打出来的安装包 +/dist/ +*.deb +*.rpm + # 构建工具 compile_commands.json CMakeLists.txt.user diff --git a/README.md b/README.md index 3a97ae7..3454eac 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 一个轻量的跨平台 Markdown 编辑器,基于 **Qt 5 / C++17** 构建。 -当前版本 **0.12.1**。 +当前版本 **0.12.2**。 ## 界面 @@ -205,6 +205,46 @@ make -C build install INSTALL_ROOT=/tmp/pkg # 打包用,装到 /tmp/pkg/usr/lo 会一起装上可执行文件、`voiletmark.desktop` 和 256×256 图标, 装完就能在应用菜单里搜到 VoiletMark。 +## 打包(Linux) + +`.github/workflows/linux-packages.yml` 是给 GitHub 镜像用的:它会编译代码并打出 +`.deb` 和 `.rpm`(本仓库在 Gitea 上,Gitea 不跑这个工作流)。 + +- 触发方式:推送到 `main`、打 tag(`v*`)、手动 **Actions → Linux 安装包 → Run workflow**、PR 检查 +- x86_64(`ubuntu-22.04`):`voiletmark_<版本>_amd64.deb` 和 `voiletmark-<版本>-1.x86_64.rpm` +- aarch64(`ubuntu-24.04-arm`):`..._arm64.deb` 和 `...aarch64.rpm`。这个 runner 公共仓库才有; + 私有仓库里 ARM 那一步设成了 `continue-on-error`,失败不会影响 x86_64 的包, + 也可以把 `runs-on` 换成自建的 arm64 runner +- 每次构建都会上传 artifact;打 tag 时自动把安装包和 `SHA256SUMS` 挂到对应的 Release + +本地打包(版本号从 `VoiletMark.pro` 里取): + +```bash +mkdir -p build && cd build +qmake ../VoiletMark.pro PREFIX=/usr && make -j"$(nproc)" +make install INSTALL_ROOT="$PWD/stage" + +version=$(sed -n 's/^VERSION[[:space:]]*=[[:space:]]*//p' ../VoiletMark.pro | head -n1) +../packaging/build-deb.sh stage "$version" amd64 ../dist +../packaging/build-rpm.sh stage "$version" x86_64 ../dist +``` + +装包: + +```bash +sudo apt install ./voiletmark_*_amd64.deb +sudo dnf install ./voiletmark-*.x86_64.rpm +``` + +要出 Release 就打个 tag(同步到 GitHub 时记得把 tag 一起推过去): + +```bash +git tag v0.12.2 && git push origin v0.12.2 +``` + +打 rpm 需要 `rpmbuild`(Ubuntu 上 `apt install rpm`)。rpm 的 `License` 字段现在写的是 +`Proprietary`(仓库里还没放 LICENSE 文件),要换许可证改 `packaging/build-rpm.sh` 里那一行即可。 + ## 图标 `logo.png` 是源文件,`packaging/generate_icons.py` 从它生成各平台图标。 @@ -265,7 +305,8 @@ src/ logo.png 图标源文件 resources.qrc 窗口图标与翻译资源 translations/ 中英文翻译(.ts / .qm) -packaging/ 各平台图标(ico / icns / png)与 .desktop +packaging/ 图标生成、.desktop、deb / rpm 打包脚本 +.github/workflows/ GitHub Actions:编译 + 打 deb / rpm ``` ## 下一版计划 diff --git a/VoiletMark.pro b/VoiletMark.pro index 1be13ab..e640370 100644 --- a/VoiletMark.pro +++ b/VoiletMark.pro @@ -1,6 +1,6 @@ TEMPLATE = app TARGET = VoiletMark -VERSION = 0.12.1 +VERSION = 0.12.2 QT += core gui widgets diff --git a/packaging/build-deb.sh b/packaging/build-deb.sh new file mode 100755 index 0000000..58ce914 --- /dev/null +++ b/packaging/build-deb.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# 把 `make install` 装出来的目录打成 .deb +# 用法: build-deb.sh <安装树> <版本> <输出目录> +set -euo pipefail + +usage() { + echo "用法: $0 <安装树> <版本> <输出目录>" >&2 + exit 2 +} + +[ $# -eq 4 ] || usage + +stage=$(cd "$1" && pwd) +version=$2 +arch=$3 +mkdir -p "$4" +out=$(cd "$4" && pwd) + +if [ ! -x "$stage/usr/bin/VoiletMark" ]; then + echo "安装树里找不到 usr/bin/VoiletMark: $stage" >&2 + exit 1 +fi + +repo=$(cd "$(dirname "$0")/.." && pwd) + +work=$(mktemp -d) +trap 'rm -rf "$work"' EXIT + +root=$work/root +mkdir -p "$root" +cp -a "$stage"/. "$root"/ +chmod 755 "$root" +find "$root" -type d -exec chmod 755 {} + +install -Dm644 "$repo/README.md" "$root/usr/share/doc/voiletmark/README.md" + +mkdir -p "$root/DEBIAN" +installed_size=$(du -sk "$root/usr" | cut -f1) + +cat > "$root/DEBIAN/control" <= 2.28), libstdc++6, libqt5core5a (>= 5.12), libqt5gui5 (>= 5.12), libqt5widgets5 (>= 5.12), libgl1 +Recommends: git +Installed-Size: $installed_size +Maintainer: liyepan +Homepage: https://git.linuxacme.com/liyepan/VoiletMark +Description: Lightweight cross-platform Markdown editor + VoiletMark is a Markdown editor built with Qt 5 and C++17. + . + Features: in-place Markdown rendering, source mode, syntax + highlighting for 11 languages, editable tables, PDF export, + folder sidebar with document outline, Git integration, Chinese + and English interface, light / dark themes. +EOF + +name=$(printf 'voiletmark_%s_%s.deb' "$version" "$arch") +dpkg-deb --build --root-owner-group "$root" "$out/$name" >/dev/null +echo "已生成 $out/$name" diff --git a/packaging/build-rpm.sh b/packaging/build-rpm.sh new file mode 100755 index 0000000..e33ca7f --- /dev/null +++ b/packaging/build-rpm.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# 把 `make install` 装出来的目录打成 .rpm(用 rpmbuild,不需要 root) +# 用法: build-rpm.sh <安装树> <版本> <输出目录> +set -euo pipefail + +usage() { + echo "用法: $0 <安装树> <版本> <输出目录>" >&2 + exit 2 +} + +[ $# -eq 4 ] || usage + +stage=$(cd "$1" && pwd) +version=$2 +arch=$3 +mkdir -p "$4" +out=$(cd "$4" && pwd) + +if [ ! -x "$stage/usr/bin/VoiletMark" ]; then + echo "安装树里找不到 usr/bin/VoiletMark: $stage" >&2 + exit 1 +fi + +repo=$(cd "$(dirname "$0")/.." && pwd) + +work=$(mktemp -d) +trap 'rm -rf "$work"' EXIT + +top=$work/rpmbuild +mkdir -p "$top/BUILD" "$top/BUILDROOT" "$top/RPMS" "$top/SOURCES" "$top/SPECS" "$top/SRPMS" +cp -a "$stage" "$top/SOURCES/root" +find "$top/SOURCES/root" -type d -exec chmod 755 {} + +install -Dm644 "$repo/README.md" "$top/SOURCES/root/usr/share/doc/voiletmark/README.md" + +cat > "$top/SPECS/voiletmark.spec" </dev/null +cp "$top/RPMS/$arch"/*.rpm "$out/" +echo "已生成 $(ls "$out"/*.rpm)"