diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e8ae8dd..4dd3134b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -359,13 +359,16 @@ jobs: - name: Cache APT packages uses: awalsh128/cache-apt-pkgs-action@latest with: - packages: clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev libasound2-dev libmpv-dev mpv keybinder-3.0 - version: 1.0 + packages: clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev libasound2-dev libmpv-dev mpv keybinder-3.0 ruby ruby-dev rubygems build-essential rpm libarchive-tools imagemagick + version: 1.1 - name: Install Linux dependencies run: | sudo apt-get update - sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev libasound2-dev libmpv-dev mpv keybinder-3.0 + sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev libasound2-dev libmpv-dev mpv keybinder-3.0 ruby ruby-dev rubygems build-essential rpm libarchive-tools imagemagick + + - name: Install fpm + run: sudo gem install fpm - name: Install dependencies run: flutter pub get @@ -378,10 +381,17 @@ jobs: cd build/linux/x64/release/bundle tar -czf $GITHUB_WORKSPACE/plezy-linux.tar.gz * - - name: Attest Linux tarball + - name: Build Linux Packages + run: OUTPUT_DIR=$GITHUB_WORKSPACE python3 linux/packaging/build-packages.py + + - name: Attest Linux artifacts uses: actions/attest-build-provenance@v2 with: - subject-path: plezy-linux.tar.gz + subject-path: | + plezy-linux.tar.gz + plezy-linux.deb + plezy-linux.rpm + plezy-linux.pkg.tar.zst - name: Upload Linux App uses: actions/upload-artifact@v4 @@ -389,6 +399,15 @@ jobs: name: linux-app path: plezy-linux.tar.gz + - name: Upload Linux Packages + uses: actions/upload-artifact@v4 + with: + name: linux-packages + path: | + plezy-linux.deb + plezy-linux.rpm + plezy-linux.pkg.tar.zst + create-release: needs: [build-android, build-ios, build-macos, build-windows, build-linux] runs-on: ubuntu-latest @@ -415,6 +434,7 @@ jobs: artifacts/windows-portable/plezy-windows-portable.7z artifacts/windows-installer/plezy-windows-installer.exe artifacts/linux-app/plezy-linux.tar.gz + artifacts/linux-packages/* draft: true prerelease: false generate_release_notes: true diff --git a/README.md b/README.md index fe422e21..122b9afe 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ A modern Plex client for desktop and mobile. Built with Flutter for native perfo ### Desktop - [Windows (x64)](https://github.com/edde746/plezy/releases/latest/download/plezy-windows-installer.exe) -- [macOS (Universal)](https://github.com/edde746/plezy/releases/latest/download/plezy-macos.zip) -- [Linux (x64)](https://github.com/edde746/plezy/releases/latest/download/plezy-linux.tar.gz) -- [NixOS/Nix](https://github.com/mio-19/nurpkgs/tree/main/pkgs/plezy) - Community package by [@mio-19](https://github.com/mio-19) via `nur.repos.mio.plezy` +- [macOS (Universal)](https://github.com/edde746/plezy/releases/latest/download/plezy-macos.dmg) +- [Linux (x64)](https://github.com/edde746/plezy/releases/latest) - .deb, .rpm, .pkg.tar.zst, and portable tar.gz available +- [NixOS/Nix](https://github.com/mio-19/nurpkgs/tree/main/pkgs/plezy) - Community package by [@mio-19](https://github.com/mio-19) ## Features diff --git a/linux/packaging/after-install.sh b/linux/packaging/after-install.sh new file mode 100644 index 00000000..682188b3 --- /dev/null +++ b/linux/packaging/after-install.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Update icon cache +if command -v gtk-update-icon-cache &> /dev/null; then + gtk-update-icon-cache -f -t /usr/share/icons/hicolor || true +fi + +# Update desktop database +if command -v update-desktop-database &> /dev/null; then + update-desktop-database /usr/share/applications || true +fi diff --git a/linux/packaging/after-remove.sh b/linux/packaging/after-remove.sh new file mode 100644 index 00000000..682188b3 --- /dev/null +++ b/linux/packaging/after-remove.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Update icon cache +if command -v gtk-update-icon-cache &> /dev/null; then + gtk-update-icon-cache -f -t /usr/share/icons/hicolor || true +fi + +# Update desktop database +if command -v update-desktop-database &> /dev/null; then + update-desktop-database /usr/share/applications || true +fi diff --git a/linux/packaging/build-packages.py b/linux/packaging/build-packages.py new file mode 100755 index 00000000..d0269f0e --- /dev/null +++ b/linux/packaging/build-packages.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +import os +import re +import subprocess +import shutil +from pathlib import Path + +# Paths +SCRIPT_DIR = Path(__file__).parent.resolve() +PROJECT_ROOT = SCRIPT_DIR.parent.parent +BUILD_DIR = PROJECT_ROOT / "build/linux/x64/release/bundle" +OUTPUT_DIR = Path(os.environ.get("OUTPUT_DIR", PROJECT_ROOT)) + +# Package metadata +METADATA = { + "name": "plezy", + "license": "Proprietary", + "vendor": "edde746", + "maintainer": "edde746 ", + "url": "https://github.com/edde746/plezy", + "description": "A modern Plex client for desktop and mobile", +} + +# Icon sizes to generate +ICON_SIZES = [16, 32, 48, 64, 128, 256, 512] + +# Distro-specific configuration +DISTROS = { + "deb": { + "type": "deb", + "arch": "amd64", + "category": "video", + "ext": "deb", + "compression": ["--deb-compression", "xz", "--deb-priority", "optional"], + "depends": [ + "libgtk-3-0", + "libmpv2 | libmpv1", + "libepoxy0", + "libkeybinder-3.0-0", + "libasound2", + "libglib2.0-0", + ], + }, + "rpm": { + "type": "rpm", + "arch": "x86_64", + "category": "Multimedia", + "ext": "rpm", + "compression": ["--rpm-compression", "xzmt"], + "depends": [ + "gtk3", + "mpv-libs", + "libepoxy", + "keybinder3", + "alsa-lib", + "glib2", + ], + }, + "pacman": { + "type": "pacman", + "arch": "x86_64", + "category": None, + "ext": "pkg.tar.zst", + "compression": ["--pacman-compression", "zstd"], + "depends": [ + "gtk3", + "mpv", + "libepoxy", + "keybinder3", + "alsa-lib", + "glib2", + ], + }, +} + + +def get_version() -> str: + """Extract version from pubspec.yaml.""" + pubspec = PROJECT_ROOT / "pubspec.yaml" + content = pubspec.read_text() + match = re.search(r"^version:\s*(.+)$", content, re.MULTILINE) + if not match: + raise RuntimeError("Could not find version in pubspec.yaml") + return match.group(1).split("+")[0] + + +def generate_icons(): + """Generate icons at multiple sizes using ImageMagick.""" + print("Generating icons...") + source = PROJECT_ROOT / "assets/plezy.png" + + for size in ICON_SIZES: + dest_dir = SCRIPT_DIR / f"icons/{size}x{size}" + dest_dir.mkdir(parents=True, exist_ok=True) + dest = dest_dir / "plezy.png" + + # Try magick (ImageMagick 7) first, then convert (ImageMagick 6) + for cmd in ["magick", "convert"]: + if shutil.which(cmd): + subprocess.run([cmd, str(source), "-resize", f"{size}x{size}", str(dest)], check=True) + break + else: + print(f"Warning: ImageMagick not found, copying original icon for {size}x{size}") + shutil.copy(source, dest) + + +def get_file_mappings() -> list[str]: + """Get file mappings for fpm.""" + mappings = [ + f"{BUILD_DIR}/=/opt/plezy/", + f"{SCRIPT_DIR}/com.edde746.plezy.desktop=/usr/share/applications/com.edde746.plezy.desktop", + f"{SCRIPT_DIR}/plezy.sh=/usr/bin/plezy", + ] + + for size in ICON_SIZES: + mappings.append( + f"{SCRIPT_DIR}/icons/{size}x{size}/plezy.png=/usr/share/icons/hicolor/{size}x{size}/apps/plezy.png" + ) + + return mappings + + +def build_package(distro: str, version: str): + """Build a package for the specified distro.""" + config = DISTROS[distro] + output_file = OUTPUT_DIR / f"{METADATA['name']}-linux.{config['ext']}" + + print(f"Building .{config['ext']} package...") + + cmd = [ + "fpm", + "-s", "dir", + "-t", config["type"], + "-n", METADATA["name"], + "-v", version, + "--iteration", "1", + "--license", METADATA["license"], + "--vendor", METADATA["vendor"], + "--maintainer", METADATA["maintainer"], + "--url", METADATA["url"], + "--description", METADATA["description"], + "--architecture", config["arch"], + ] + + if config["category"]: + cmd.extend(["--category", config["category"]]) + + for dep in config["depends"]: + cmd.extend(["--depends", dep]) + + cmd.extend(config["compression"]) + + cmd.extend([ + "--after-install", str(SCRIPT_DIR / "after-install.sh"), + "--after-remove", str(SCRIPT_DIR / "after-remove.sh"), + "--package", str(output_file), + ]) + + cmd.extend(get_file_mappings()) + + subprocess.run(cmd, check=True) + print(f"Created: {output_file}") + + +def main(): + # Verify build exists + if not BUILD_DIR.exists(): + print(f"Error: Build directory not found at {BUILD_DIR}") + print("Please run 'flutter build linux --release' first") + exit(1) + + version = get_version() + print(f"Building packages for {METADATA['name']} version {version}") + + # Make scripts executable + for script in ["plezy.sh", "after-install.sh", "after-remove.sh"]: + (SCRIPT_DIR / script).chmod(0o755) + + # Generate icons + generate_icons() + + # Build all packages + for distro in DISTROS: + build_package(distro, version) + + print("\nAll packages built successfully!") + for f in OUTPUT_DIR.glob(f"{METADATA['name']}-linux.*"): + print(f" {f}") + + +if __name__ == "__main__": + main() diff --git a/linux/packaging/com.edde746.plezy.desktop b/linux/packaging/com.edde746.plezy.desktop new file mode 100644 index 00000000..da559558 --- /dev/null +++ b/linux/packaging/com.edde746.plezy.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=Plezy +Comment=A modern Plex client for desktop and mobile +Exec=/opt/plezy/plezy +Icon=plezy +Terminal=false +Categories=AudioVideo;Video;Player; +Keywords=plex;media;streaming;video; +StartupWMClass=plezy diff --git a/linux/packaging/plezy.sh b/linux/packaging/plezy.sh new file mode 100644 index 00000000..29a0bcb0 --- /dev/null +++ b/linux/packaging/plezy.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd /opt/plezy +exec ./plezy "$@"