From dd7c8162acef8893ea9f37cc9595f19111d573b3 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Mon, 9 Jun 2025 18:38:53 +0200 Subject: [PATCH] Update appimage build --- .github/workflows/build_linux.yml | 14 +++- pkgutils.py | 24 ++---- setup/icons/novelwriter.png | Bin 0 -> 9228 bytes setup/icons/x-novelwriter-project.png | Bin 0 -> 8843 bytes utils/build_appimage.py | 112 ++++++++++---------------- utils/common.py | 37 +++++++-- 6 files changed, 86 insertions(+), 101 deletions(-) create mode 100644 setup/icons/novelwriter.png create mode 100644 setup/icons/x-novelwriter-project.png diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 8a926afc..ca073f1f 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -10,15 +10,21 @@ jobs: needs: buildAssets runs-on: ubuntu-latest env: - PYTHON_VERSION: "3.11" - LINUX_TAG: "manylinux_2_28_x86_64" + PYTHON_VERSION: "3.13" + LINUX_TAG: "manylinux_2_28" + LINUX_ARCH: "x86_64" steps: - name: Python Setup uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.13" architecture: x64 + - name: Install Packages (apt) + run: | + sudo apt update + sudo apt install libxcb-cursor0 + - name: Install Packages (pip) run: pip install python-appimage setuptools @@ -35,7 +41,7 @@ jobs: id: build run: | echo "BUILD_VERSION=$(python pkgutils.py version)" >> $GITHUB_OUTPUT - python pkgutils.py build-appimage --linux-tag $LINUX_TAG --python-version $PYTHON_VERSION + python pkgutils.py build-appimage $LINUX_TAG $LINUX_ARCH $PYTHON_VERSION - name: Upload Artifacts uses: actions/upload-artifact@v4 diff --git a/pkgutils.py b/pkgutils.py index fc30b985..beb5fece 100755 --- a/pkgutils.py +++ b/pkgutils.py @@ -272,24 +272,12 @@ if __name__ == "__main__": cmdBuildUbuntu.set_defaults(func=utils.build_debian.launchpad) # Build AppImage - cmdBuildAppImage = parsers.add_parser( - "build-appimage", help=( - "Build an AppImage. " - "Argument --linux-tag defaults manylinux_2_28_x86_64, and --python-version to 3.13." - ) - ) - cmdBuildAppImage.add_argument( - "--linux-tag", - default="manylinux_2_28_x86_64", - help=( - "Linux compatibility tag (e.g. manylinux_2_28_x86_64) " - "see https://python-appimage.readthedocs.io/en/latest/#available-python-appimages " - "and https://github.com/pypa/manylinux for a list of valid tags." - ), - ) - cmdBuildAppImage.add_argument( - "--python-version", default="3.13", help="Python version (e.g. 3.13)" - ) + # See https://github.com/pypa/manylinux + # See https://python-appimage.readthedocs.io/en/latest/#available-python-appimages + cmdBuildAppImage = parsers.add_parser("build-appimage", help="Build an AppImage.") + cmdBuildAppImage.add_argument("linux", help="Manylinux version, e.g. manylinux_2_28.") + cmdBuildAppImage.add_argument("arch", help="Architecture, e.g. x86_64.") + cmdBuildAppImage.add_argument("python", help="Python version, e.g. 3.13.") cmdBuildAppImage.set_defaults(func=utils.build_appimage.appImage) # Build Windows Inno Setup Installer diff --git a/setup/icons/novelwriter.png b/setup/icons/novelwriter.png new file mode 100644 index 0000000000000000000000000000000000000000..a9ec557a12a6a78155e501a2bf039348d3ae04f9 GIT binary patch literal 9228 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 LtgTcIw~F{5k2VKv literal 0 HcmV?d00001 diff --git a/setup/icons/x-novelwriter-project.png b/setup/icons/x-novelwriter-project.png new file mode 100644 index 0000000000000000000000000000000000000000..9186479b30c170e27977aa4d0fd277ac0d80e6f2 GIT binary patch literal 8843 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 Xy5&$|MUj+%u|dzCsw$MrnT7l>1rdiP literal 0 HcmV?d00001 diff --git a/utils/build_appimage.py b/utils/build_appimage.py index ea784269..57b44413 100644 --- a/utils/build_appimage.py +++ b/utils/build_appimage.py @@ -22,13 +22,15 @@ from __future__ import annotations import argparse import datetime +import os import shutil -import subprocess import sys +from pathlib import Path + from utils.common import ( ROOT_DIR, SETUP_DIR, appdataXml, copyPackageFiles, copySourceCode, - extractVersion, makeCheckSum, toUpload, writeFile + extractVersion, freshFolder, makeCheckSum, systemCall, toUpload, writeFile ) @@ -49,115 +51,83 @@ def appImage(args: argparse.Namespace) -> None: print("") print("Build AppImage") - print("==============") - print("") + print("="*120) - linuxTag = args.linux_tag - pythonVer = args.python_version + mLinux = args.linux + mArch = args.arch + pyVer = args.python # Version Info - # ============ - pkgVers, _, relDate = extractVersion() relDate = datetime.datetime.strptime(relDate, "%Y-%m-%d") - print("") # Set Up Folder - # ============= - bldDir = ROOT_DIR / "dist_appimage" - bldPkg = f"novelwriter_{pkgVers}" + bldPkg = f"novelwriter-{pkgVers}-{mArch}" + bldImg = f"{bldPkg}.AppImage" outDir = bldDir / bldPkg imgDir = bldDir / "appimage" - - # Set Up Folders - # ============== + appDir = bldDir / f"novelWriter-{mArch}" bldDir.mkdir(exist_ok=True) - - if outDir.exists(): - print("Removing old build files ...") - print("") - shutil.rmtree(outDir) - - outDir.mkdir() - - if imgDir.exists(): - print("Removing old build metadata files ...") - print("") - shutil.rmtree(imgDir) - - imgDir.mkdir() + freshFolder(outDir) + freshFolder(imgDir) + freshFolder(appDir) # Remove old AppImages if images := bldDir.glob("*.AppImage"): print("Removing old AppImages") - print("") for image in images: image.unlink() # Copy novelWriter Source - # ======================= - print("Copying novelWriter source ...") - print("") - copySourceCode(outDir) - print("") print("Copying or generating additional files ...") - print("") - copyPackageFiles(outDir) # Write Metadata - # ============== - writeFile(imgDir / "novelwriter.appdata.xml", appdataXml()) - print("Wrote: novelwriter.appdata.xml") - + writeFile(imgDir / "requirements.txt", str(outDir)) writeFile(imgDir / "entrypoint.sh", ( - '#! /bin/bash \n' + f"export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${{APPDIR}}/usr/lib/{mArch}-linux-gnu/\n" '{{ python-executable }} -sE ${APPDIR}/opt/python{{ python-version }}/bin/novelwriter "$@"' )) - print("Wrote: entrypoint.sh") - - writeFile(imgDir / "requirements.txt", str(outDir)) - print("Wrote: requirements.txt") shutil.copyfile(SETUP_DIR / "data" / "novelwriter.desktop", imgDir / "novelwriter.desktop") print("Copied: novelwriter.desktop") - shutil.copyfile(SETUP_DIR / "icons" / "novelwriter.svg", imgDir / "novelwriter.svg") - print("Copied: novelwriter.svg") - - shutil.copyfile( - SETUP_DIR / "data" / "hicolor" / "256x256" / "apps" / "novelwriter.png", - imgDir / "novelwriter.png" - ) + shutil.copyfile(SETUP_DIR / "icons" / "novelwriter.png", imgDir / "novelwriter.png") print("Copied: novelwriter.png") - # Build AppImage - # ============== + # Build AppDir + systemCall([ + sys.executable, "-m", "python_appimage", "build", "app", "--no-packaging", + "-l", f"{mLinux}_{mArch}", "-p", pyVer, "appimage" + ], cwd=bldDir) - try: - subprocess.call([ - sys.executable, "-m", "python_appimage", "build", "app", - "-l", linuxTag, "-p", pythonVer, "appimage" - ], cwd=bldDir) - except Exception as exc: - print("AppImage build: FAILED") - print("") - print(str(exc)) - print("") - sys.exit(1) + # Copy Libraries + libPath = Path(f"/usr/lib/{mArch}-linux-gnu") + appLibs = appDir / "usr" / "lib" / f"{mArch}-linux-gnu" + appLibs.mkdir(exist_ok=True) + shutil.copyfile(libPath / "libxcb-cursor.so.0", appLibs / "libxcb-cursor.so.0") - bldFile = list(bldDir.glob("*.AppImage"))[0] - outFile = bldDir / f"novelWriter-{pkgVers}.AppImage" - bldFile.rename(outFile) - shaFile = makeCheckSum(outFile.name, cwd=bldDir) + # Build Image + env = os.environ.copy() + env["ARCH"] = mArch + systemCall([ + "appimagetool", "--no-appstream", "--updateinformation", + f"gh-releases-zsync|vkbo|novelwriter|latest|novelwriter-*-{mArch}.AppImage.zsync", + str(appDir), bldImg + ], cwd=bldDir, env=env) - toUpload(outFile) + updFile = bldDir / f"{bldImg}.zsync" + bldFile = bldDir / bldImg + shaFile = makeCheckSum(bldFile.name, cwd=bldDir) + + toUpload(bldFile) + toUpload(updFile) toUpload(shaFile) return diff --git a/utils/common.py b/utils/common.py index 6eda3841..2c986480 100644 --- a/utils/common.py +++ b/utils/common.py @@ -22,6 +22,7 @@ from __future__ import annotations import shutil import subprocess +import sys from pathlib import Path @@ -77,16 +78,16 @@ def copySourceCode(dst: Path) -> None: for item in src.glob("**/*"): relSrc = item.relative_to(ROOT_DIR) if item.suffix in (".pyc", ".pyo"): - print(f"Ignore: {relSrc}") + print("Ignored:", relSrc) continue if item.parent.is_dir() and item.parent.name != "__pycache__": dstDir = dst / relSrc.parent if not dstDir.exists(): dstDir.mkdir(parents=True) - print(f"Folder: {dstDir}") + print("Created:", dstDir.relative_to(ROOT_DIR)) if item.is_file(): shutil.copyfile(item, dst / relSrc) - print(f"Copied: {dst / relSrc}") + print("Copied:", relSrc) return @@ -95,26 +96,23 @@ def copyPackageFiles(dst: Path, setupPy: bool = False) -> None: copyFiles = ["LICENSE.md", "CREDITS.md", "pyproject.toml"] for copyFile in copyFiles: shutil.copyfile(copyFile, dst / copyFile) - print(f"Copied: {copyFile}") + print("Copied:", copyFile) writeFile(dst / "MANIFEST.in", ( "include LICENSE.md\n" "include CREDITS.md\n" "recursive-include novelwriter/assets *\n" )) - print("Wrote: MANIFEST.in") if setupPy: writeFile(dst / "setup.py", ( "import setuptools\n" "setuptools.setup()\n" )) - print("Wrote: setup.py") text = readFile(ROOT_DIR / "pyproject.toml") text = text.replace("setup/description_pypi.md", "data/description_short.txt") writeFile(dst / "pyproject.toml", text) - print("Wrote: pyproject.toml") return @@ -188,4 +186,27 @@ def readFile(file: Path) -> str: def writeFile(file: Path, text: str) -> int: """Write string to file.""" - return file.write_text(text, encoding="utf-8") + result = file.write_text(text, encoding="utf-8") + print("Wrote:", file.relative_to(ROOT_DIR)) + return result + + +def freshFolder(path: Path) -> None: + """Make sure a folder exists and is empty.""" + if path.exists(): + print("Removing:", str(path)) + shutil.rmtree(path) + path.mkdir() + return + + +def systemCall(cmd: list, cwd: Path | str | None = None, env: dict | None = None) -> None: + """Make a system call using subprocess.""" + if isinstance(cwd, Path): + cwd = str(cwd) + try: + subprocess.call([str(c) for c in cmd], cwd=cwd, env=env) + except Exception as exc: + print("ERROR:", str(exc)) + sys.exit(1) + return