From a31fa375c95d703a173fbb5af13b4be7b09460e3 Mon Sep 17 00:00:00 2001 From: guihkx <626206+guihkx@users.noreply.github.com> Date: Sat, 20 May 2023 18:00:25 -0300 Subject: [PATCH] linux: add support for AppImage packages AppImage is a more flexible packaging solution for Linux than what PyInstaller currently offers. Some of the benefits are: - Ability to ship the app with predefined environment variables (solving issues like #321) - Improved integration with the desktop environment (if you're using go-appimage[1] or libappimage[2]) - Much lighter in size (~27 MB difference) To actually make the AppImage, I'm using appimage-builder[3], which makes the process a bit easier. For more details about the AppImage technology, check out their website[4]. [1] https://github.com/probonopd/go-appimage [2] https://github.com/AppImageCommunity/libappimage [3] https://appimage-builder.readthedocs.io/en/latest/index.html [4] https://appimage.org/ --- .gitignore | 6 ++ appimage/AppImageBuilder.yml | 144 +++++++++++++++++++++++++++++++++++ appimage/pickaxe.png | Bin 0 -> 2287 bytes constants.py | 25 +++--- 4 files changed, 166 insertions(+), 9 deletions(-) create mode 100644 appimage/AppImageBuilder.yml create mode 100644 appimage/pickaxe.png diff --git a/.gitignore b/.gitignore index 117407d1..a71ef047 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,9 @@ log.txt /lock.file settings.json /lang/English.json +# AppImage +/AppDir +/appimage-builder +/appimage-build +/*.AppImage +/*.AppImage.zsync diff --git a/appimage/AppImageBuilder.yml b/appimage/AppImageBuilder.yml new file mode 100644 index 00000000..612c8ace --- /dev/null +++ b/appimage/AppImageBuilder.yml @@ -0,0 +1,144 @@ +# Useful links: +# https://appimage-builder.readthedocs.io/en/latest/reference/recipe.html + +version: 1 + +script: + # Clean up any previous builds. + - rm -rf "$BUILD_DIR" "$TARGET_APPDIR" || true + - mkdir -p "$BUILD_DIR" "$TARGET_APPDIR" + - cd "$BUILD_DIR" + + # Build a recent version of libXft first. This fixes an issue where the app won't start with libXft 2.3.3 if an emoji font is installed on the system. + - curl -L https://xorg.freedesktop.org/releases/individual/lib/libXft-2.3.8.tar.xz -o libXft.tar.xz + - tar xvf libXft.tar.xz + - cd libXft-2.3.8 + - ./configure --prefix="$TARGET_APPDIR/usr" --disable-static + - make -j$(nproc) + - make install-strip + + # Package the app. + - mkdir -p "$TARGET_APPDIR"/usr/{src,share/icons/hicolor/128x128/apps} + - cp -r "$SOURCE_DIR/../lang" "$SOURCE_DIR/../pickaxe.ico" "$SOURCE_DIR"/../*.py "$TARGET_APPDIR/usr/src" + - cp "$SOURCE_DIR/pickaxe.png" "$TARGET_APPDIR/usr/share/icons/hicolor/128x128/apps/io.github.devilxd.twitchdropsminer.png" + + # Remove certifi from the line below if using Python 3.10+. + - python3.8 -m pip install --ignore-installed --prefix=/usr --root="$TARGET_APPDIR" -r "$SOURCE_DIR/../requirements.txt" certifi + +AppDir: + app_info: + id: io.github.devilxd.twitchdropsminer + name: Twitch Drops Miner + icon: io.github.devilxd.twitchdropsminer + version: '{{APP_VERSION}}' + exec: usr/bin/python3.8 + exec_args: '${APPDIR}/usr/src/main.py $@' + + apt: + arch: amd64 + sources: + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-backports main universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-security main universe + key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32 + + include: + - gir1.2-appindicator3-0.1 + - python3-tk + + exclude: + - adwaita-icon-theme + - dconf-service + - glib-networking-services + - gsettings-desktop-schemas + - hicolor-icon-theme + - humanity-icon-theme + - libavahi-client3 + - libavahi-common3 + - libbrotli1 + - libcolord2 + - libcups2 + - libdb5.3 + - libdconf1 + - libgmp10 + - libgnutls30 + - libgssapi-krb5-2 + - libhogweed5 + - libicu66 + - libjson-glib-1.0-0 + - libk5crypto3 + - libkrb5-3 + - libkrb5support0 + - liblcms2-2 + - libncursesw6 + - libnettle7 + - libp11-kit0 + - libpangoxft-1.0-0 + - libpsl5 + - librest-0.7-0 + - libsoup2.4-1 + - libsoup-gnome2.4-1 + - libsqlite3-0 + - libtasn1-6 + - libtiff5 + - libtinfo6 + - libunistring2 + - libwebp6 + - libxft2 # We'll ship our own, updated version of this library. + - libxml2 + - mime-support + - readline-common + - tzdata + - xkb-data + + files: + exclude: + - etc + - usr/bin/normalizer + - usr/bin/pdb3* + - usr/bin/py3* + - usr/bin/pydoc* + - usr/bin/pygettext3* + - usr/include + # The next 2 files come from our own build of libXft, and they can be removed. + - usr/lib/libXft.la + - usr/lib/pkgconfig + - usr/lib/python3.9 + - usr/lib/valgrind + - usr/lib/*-linux-gnu/engines-1.1 + - usr/lib/*-linux-gnu/glib-2.0 + - usr/lib/*-linux-gnu/gtk-3.0 + - usr/lib/*-linux-gnu/libgtk-3.0 + - usr/share/applications + - usr/share/binfmts + - usr/share/bug + - usr/share/doc + - usr/share/doc-base + - usr/share/glib-2.0 + - usr/share/lintian + - usr/share/man + - usr/share/pixmaps + - usr/share/python3 + - usr/share/themes + + runtime: + env: + PATH: '${APPDIR}/usr/bin:${PATH}' + PYTHONHOME: '${APPDIR}/usr' + PYTHONPATH: '${APPDIR}/usr/lib/python3.8/tkinter:${APPDIR}/usr/lib/python3.8/site-packages' + APPDIR_LIBRARY_PATH: '${APPDIR}/usr/lib:${APPDIR}/usr/lib/x86_64-linux-gnu:${APPDIR}/lib/x86_64-linux-gnu' + TCL_LIBRARY: '${APPDIR}/usr/share/tcltk/tcl8.6' + TK_LIBRARY: '${APPDIR}/usr/lib/tcltk/x86_64-linux-gnu/tk8.6' + TKPATH: '${APPDIR}/usr/lib/tcltk/x86_64-linux-gnu/tk8.6' + # Remove the line below if using Python 3.10+. + SSL_CERT_FILE: '${APPDIR}/usr/lib/python3.8/site-packages/certifi/cacert.pem' + # The app seems to have problems running on Wayland at the moment. + # See: https://github.com/DevilXD/TwitchDropsMiner/issues/321 + GDK_BACKEND: x11 + +AppImage: + arch: x86_64 + file_name: Twitch.Drops.Miner-x86_64.AppImage + sign-key: None + update-information: guess diff --git a/appimage/pickaxe.png b/appimage/pickaxe.png new file mode 100644 index 0000000000000000000000000000000000000000..40ceefa21a0db962cb2618c2ddb8361cdc842a88 GIT binary patch literal 2287 zcmeH{X;2eb631T*gi{XJa0G+^9=LKOgqxrwCL)j+jv&kEMDD{FK_=l)ju2r%@BmQ} zK?tG(igAQsmiv-JV31`IlmJE-h(KU~K`xbSrnYLUcB}ULe%Pw+{#U>5Uv<}eRqu-D z3D=#<2xS1kPPgL(Z<(@xcSWcyu8MLuWr7So?o9^Z;(pnA8US0r*FOWmLIE%r0D$9N z0JNiv>b%R_`?NRqQG<`6BWd;U25tmUBsL#0P;`V z2smo|*h1m_Q9YtYtEH0u-d`^3c(@G>A-xG+IOFu8psW|jLFJcxd}O7!dF|W7{FlD- z^z92bO!hkE3mXcA6HDovTGg1}`W7XN5=oGMcZ^y01Jf7r#*=Ru!dUEip>xygNoszQ z@dSFkz-+_AqCirEnQf%SMZqTz7t9~A@jRD!y>DFb@D}Md2JO5^DRJOC#*b{?6?l{` z7T$dJP_mk1-0H+>`xw`!x}MQ-Q^#vLMNL;Pv>{G2NEx?G+nfK4OL{+zw-}|ns%5^& z>ZuoD@kYB38?sHC`c7b z`>QTUZSN&AR-&3ri*sy5=|WW}e2eB<)~k z7pu)ARb3NhIY><&loWRvYUek~ok2h~Ot^#J=2w2s=eoAJ=BQ4m0gd%volXqZEcPpFfUsvF;QUS zlyKF{Nq_7a6nrq^V1{unKTM=DV%o&b5Uk>GlIB5Q?eF(qAwF~+B`p}A627uPl0<|E zIfI_@q>$|RfDV93CL{7%)oN=qtsXMQ0(GIS>xan*u_gzt4{PuO-$q_FekI=w+F2fP zgaGWPN4tJhRM|}zj}n4ydS8BqRBSOqZnF>0ym%HY{+#ivZZ& zSKMA`MrzepbNVGjSafw(SkvF!dcXF7c(jhnp%HwJ+4t2NVFmtvI`1kMr)ZK9PqV6nPPJ8#0A{u%@r)KNtJetu z@W-mjv@7+vaH173Z#&ydr`Emhot*`6Ew)YSt?5pTBcP-CRSn1elAi>D#!oDWMd8&x zSSlnsCRbjg<4Xga1x`+PYEp15u6wv!PAW*L!qA;ts`daiFwapPSvykxGZ0Y>aCRe; zz6$TAIzN^rV-=s@hKP1OSHb$`?PGyC$gJmNzygHD)}7qbB<4~JePQgwlW*UR%RhAH zsR6w{Gmh946Jgo51Q8kB5Se*fL;&_anm+beFBAa+@>u(FUFyH9AON?kUQbA@PniYm z{G#kqA|Kg2xC5{TE|+FJm?1j@Pcc?oqo!;R88oXjkka%MzBuZ+ti|Tk*a^U1>Y~_Z^pvm?BoIYMHrAeI% z;Zl|%x2?d{^pUci(AK0!8BEzKYm!Vz5#e@R(2zR7IC+ikC4&>i8*zLAa|{$c3v}-F zh`9#9zIL1YUVwc{aX^MPt~){8@lgHCz*9ZtXs{N0^uB84#^@4b%U?N`!|3Q6%rYi@ZW*$S?(?wVEV5K%t%J)Ps~6@^nWmD8%JyVe-05` R7_yTwz>Ro Path: Works for dev and for PyInstaller. """ - if IS_PACKAGED: + if IS_APPIMAGE: + base_path = Path(sys.argv[0]).absolute().parent + elif IS_PACKAGED: # PyInstaller's folder where the one-file app is unpacked meipass: str = getattr(sys, "_MEIPASS") base_path = Path(meipass) @@ -73,12 +77,15 @@ def _merge_vars(base_vars: JsonType, vars: JsonType) -> None: # Base Paths -# NOTE: pyinstaller will set this to its own executable when building, -# detect this to use __file__ and main.py redirection instead -SELF_PATH = Path(sys.argv[0]).absolute() -if SELF_PATH.stem == "pyinstaller": - SELF_PATH = Path(__file__).with_name("main.py").absolute() -WORKING_DIR = SELF_PATH.absolute().parent +if IS_APPIMAGE: + SELF_PATH = Path(os.environ["APPIMAGE"]).absolute() +else: + # NOTE: pyinstaller will set sys.argv[0] to its own executable when building, + # detect this to use __file__ and main.py redirection instead + SELF_PATH = Path(sys.argv[0]).absolute() + if SELF_PATH.stem == "pyinstaller": + SELF_PATH = Path(__file__).with_name("main.py").absolute() +WORKING_DIR = SELF_PATH.parent # Development paths VENV_PATH = Path(WORKING_DIR, "env") SITE_PACKAGES_PATH = Path(VENV_PATH, SYS_SITE_PACKAGES)