name: Build Linux Release on: push: tags: - 'v*' permissions: contents: write env: PYTHON_VERSION: '3.13.6' jobs: build-linux: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Extract version from tag id: get_version shell: bash run: | tag="${GITHUB_REF_NAME}" version="${tag#v}" echo "Extracted version: $version" echo "VERSION=$version" >> "$GITHUB_OUTPUT" - name: Setup Python uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} - name: Cache Python dependencies uses: actions/cache@v3 with: path: | venv ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - name: Install system dependencies shell: bash run: | sudo apt-get update -y # Tools commonly required by cx_Freeze on Linux packaging sudo apt-get install -y --no-install-recommends \ rpm alien fakeroot patchelf desktop-file-utils file xz-utils zsync - name: Create virtual environment and install dependencies shell: bash run: | python -m venv venv source venv/bin/activate python -m pip install --upgrade pip pip install --no-cache-dir -r requirements.txt pip install --no-cache-dir cx_Freeze yt-dlp - name: Prepare build variables shell: bash run: | version="${{ steps.get_version.outputs.VERSION }}" echo "VERSION=$version" >> "$GITHUB_ENV" arch="$(uname -m)" # x86_64 or aarch64 echo "ARCH=$arch" >> "$GITHUB_ENV" echo "Prepared build variables for version: $version on $(uname -a)" - name: Create cx_Freeze setup script (Linux) shell: bash run: | cat > setup_cxfreeze.py <<'PY' import os from cx_Freeze import setup, Executable version = os.environ.get("VERSION", "0.0.0") build_exe_options = dict( optimize=2, packages=[ "PySide6.QtCore", "PySide6.QtGui", "PySide6.QtWidgets", "requests", "PIL", "packaging", "markdown", "pyglet", "loguru", "setuptools", ], excludes=[ "PySide6.QtBluetooth", "PySide6.QtNetwork", "PySide6.QtOpenGL", "PySide6.QtPrintSupport", "PySide6.QtSvg", "PySide6.QtTest", "PySide6.QtXml", "PySide6.QtSql", "PySide6.QtHelp", "PySide6.QtMultimedia", "PySide6.QtQml", "PySide6.QtQuick", "PySide6.QtWebEngineCore", "PIL.ImageDraw", "PIL.ImageFont", "numpy", "scipy", "wx", "pandas", "tkinter", "yt_dlp", "unittest", "test", "tests", ], include_files=[ ("src", "src"), ("assets", "assets"), ], ) executables = [ Executable( script="main.py", target_name="YTSage", icon="assets/branding/icons/icon.png", ) ] setup( name="YTSage", version=version, description="YTSage", options={ "build_exe": build_exe_options, # AppImage packaging "bdist_appimage": { # If ends with .AppImage, use verbatim file name "target_name": f"YTSage-v{version}.AppImage", # Use the base executable name internally, version in AppImage filename }, # RPM packaging metadata (minimal) "bdist_rpm": { "release": "1", "group": "Applications/Multimedia", # Optional: icon can be picked from Executable's icon }, # DEB packaging wraps alien to convert RPM "bdist_deb": {}, }, executables=executables, ) PY - name: Build AppImage, RPM and DEB shell: bash run: | set -e source venv/bin/activate # Ensure a clean build and build_exe first python setup_cxfreeze.py build_exe # Remove screenshots to reduce size before packaging build_dir=$(ls -d build/exe.* 2>/dev/null | head -n1 || true) if [ -n "$build_dir" ] && [ -d "$build_dir/assets/branding/screenshots" ]; then rm -rf "$build_dir/assets/branding/screenshots" echo "Removed screenshots folder from $build_dir" fi # Build AppImage python setup_cxfreeze.py bdist_appimage # Build RPM python setup_cxfreeze.py bdist_rpm # Build DEB (via alien from RPM) python setup_cxfreeze.py bdist_deb || echo "bdist_deb completed with warnings (alien)" echo "Post-build directory listing:" echo "-- dist --"; ls -lah dist || true echo "-- build --"; ls -lah build || true - name: Package and prepare release artifacts (.AppImage, .rpm, .deb) shell: bash run: | version="${{ steps.get_version.outputs.VERSION }}" mkdir -p artifacts # Copy AppImage appimage_src=$(ls dist/*.AppImage 2>/dev/null | head -n1 || true) if [ -n "$appimage_src" ]; then cp "$appimage_src" "artifacts/YTSage-v${version}.AppImage" echo "Copied AppImage: $appimage_src -> artifacts/YTSage-v${version}.AppImage" else echo "Warning: No .AppImage found in dist/" fi # Copy RPM (normalize name) rpm_src=$(ls dist/*.rpm 2>/dev/null | head -n1 || true) if [ -n "$rpm_src" ]; then cp "$rpm_src" "artifacts/YTSage-v${version}.rpm" echo "Copied RPM: $rpm_src -> artifacts/YTSage-v${version}.rpm" else echo "Warning: No .rpm found in dist/" fi # Copy DEB (normalize name) deb_src=$(ls dist/*.deb 2>/dev/null | head -n1 || true) if [ -n "$deb_src" ]; then cp "$deb_src" "artifacts/YTSage-v${version}.deb" echo "Copied DEB: $deb_src -> artifacts/YTSage-v${version}.deb" else echo "Warning: No .deb found in dist/" fi echo "Final artifacts:" ls -lh artifacts || true - name: Create/Update draft release uses: softprops/action-gh-release@v1 with: tag_name: ${{ github.ref_name }} name: YTSage ${{ github.ref_name }} draft: true prerelease: false append_body: true fail_on_unmatched_files: false body: | # YTSage ${{ github.ref_name }} **Release Date**: ${{ github.event.head_commit.timestamp }} **Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ github.ref_name }} files: | artifacts/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}