1b552c4ae8
Enhanced the workflow to handle multiple data archive compression formats (xz, gz, zst) when extracting and repackaging DEB files. Added checks for the presence of the data archive and updated extraction and repackaging commands to match the detected format, improving robustness and compatibility.
354 lines
11 KiB
YAML
354 lines
11 KiB
YAML
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"),
|
|
("ytsage.desktop", "share/applications/ytsage.desktop"),
|
|
("assets/branding/icons/icon.png", "share/pixmaps/ytsage.png"),
|
|
],
|
|
)
|
|
|
|
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",
|
|
},
|
|
# RPM packaging metadata
|
|
"bdist_rpm": {
|
|
"release": "1",
|
|
"group": "Applications/Multimedia",
|
|
},
|
|
# DEB packaging wraps alien to convert RPM
|
|
"bdist_deb": {},
|
|
},
|
|
executables=executables,
|
|
)
|
|
PY
|
|
|
|
- name: Create desktop entry
|
|
shell: bash
|
|
run: |
|
|
cat > ytsage.desktop <<'DESKTOP'
|
|
[Desktop Entry]
|
|
Type=Application
|
|
Name=YTSage
|
|
Comment=YouTube downloader
|
|
Exec=ytsage %U
|
|
Icon=ytsage
|
|
Terminal=false
|
|
Categories=AudioVideo;Network;Utility;
|
|
StartupWMClass=YTSage
|
|
DESKTOP
|
|
|
|
- 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)"
|
|
|
|
# Fix DEB desktop integration (alien sometimes misses this)
|
|
deb_file=$(ls dist/ytsage_*.deb 2>/dev/null | head -n1 || true)
|
|
if [ -n "$deb_file" ]; then
|
|
echo "Fixing DEB desktop integration for: $deb_file"
|
|
|
|
# Create temporary directory for DEB extraction
|
|
temp_deb_dir=$(mktemp -d)
|
|
cd "$temp_deb_dir"
|
|
|
|
# Extract the DEB package
|
|
ar x "$GITHUB_WORKSPACE/$deb_file"
|
|
|
|
# List extracted files to see what we have
|
|
echo "Extracted DEB contents:"
|
|
ls -la
|
|
|
|
# Find the data archive (could be data.tar.xz, data.tar.gz, data.tar.zst, etc.)
|
|
data_archive=""
|
|
for ext in xz gz zst; do
|
|
if [ -f "data.tar.$ext" ]; then
|
|
data_archive="data.tar.$ext"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -z "$data_archive" ]; then
|
|
echo "Warning: Could not find data archive in DEB package"
|
|
cd "$GITHUB_WORKSPACE"
|
|
rm -rf "$temp_deb_dir"
|
|
else
|
|
echo "Found data archive: $data_archive"
|
|
|
|
# Extract the data archive
|
|
mkdir -p data
|
|
cd data
|
|
|
|
case "$data_archive" in
|
|
*.tar.xz)
|
|
tar xf "../$data_archive"
|
|
;;
|
|
*.tar.gz)
|
|
tar xzf "../$data_archive"
|
|
;;
|
|
*.tar.zst)
|
|
tar --zstd -xf "../$data_archive"
|
|
;;
|
|
esac
|
|
|
|
# Ensure desktop files are properly placed
|
|
if [ ! -d "usr/share/applications" ]; then
|
|
mkdir -p usr/share/applications
|
|
fi
|
|
if [ ! -d "usr/share/pixmaps" ]; then
|
|
mkdir -p usr/share/pixmaps
|
|
fi
|
|
|
|
# Copy desktop entry if it exists in the package but not in the right place
|
|
if [ -f "usr/lib/ytsage-${version}/share/applications/ytsage.desktop" ] && [ ! -f "usr/share/applications/ytsage.desktop" ]; then
|
|
cp "usr/lib/ytsage-${version}/share/applications/ytsage.desktop" "usr/share/applications/"
|
|
echo "Copied desktop entry to proper location"
|
|
fi
|
|
|
|
# Copy icon if it exists in the package but not in the right place
|
|
if [ -f "usr/lib/ytsage-${version}/share/pixmaps/ytsage.png" ] && [ ! -f "usr/share/pixmaps/ytsage.png" ]; then
|
|
cp "usr/lib/ytsage-${version}/share/pixmaps/ytsage.png" "usr/share/pixmaps/"
|
|
echo "Copied icon to proper location"
|
|
fi
|
|
|
|
# Repackage the data archive with the same compression
|
|
case "$data_archive" in
|
|
*.tar.xz)
|
|
tar cJf "../$data_archive" ./*
|
|
;;
|
|
*.tar.gz)
|
|
tar czf "../$data_archive" ./*
|
|
;;
|
|
*.tar.zst)
|
|
tar --zstd -cf "../$data_archive" ./*
|
|
;;
|
|
esac
|
|
cd ..
|
|
|
|
# Create the fixed DEB package
|
|
ar r "${GITHUB_WORKSPACE}/${deb_file}" debian-binary control.tar.* "$data_archive"
|
|
|
|
# Clean up
|
|
cd "$GITHUB_WORKSPACE"
|
|
rm -rf "$temp_deb_dir"
|
|
|
|
echo "DEB package desktop integration fixed"
|
|
fi
|
|
fi
|
|
|
|
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/ytsage-v*.AppImage 2>/dev/null | head -n1 || true)
|
|
if [ -n "$appimage_src" ]; then
|
|
cp "$appimage_src" "artifacts/YTSage-v${version}-${ARCH}.AppImage"
|
|
echo "Copied AppImage: $appimage_src -> artifacts/YTSage-v${version}-${ARCH}.AppImage"
|
|
else
|
|
echo "Warning: No .AppImage found in dist/"
|
|
fi
|
|
|
|
# Copy RPM (normalize name)
|
|
rpm_src=$(ls dist/ytsage-*.rpm 2>/dev/null | head -n1 || true)
|
|
if [ -n "$rpm_src" ]; then
|
|
cp "$rpm_src" "artifacts/YTSage-v${version}-${ARCH}.rpm"
|
|
echo "Copied RPM: $rpm_src -> artifacts/YTSage-v${version}-${ARCH}.rpm"
|
|
else
|
|
echo "Warning: No .rpm found in dist/"
|
|
fi
|
|
|
|
# Copy DEB (normalize name)
|
|
deb_src=$(ls dist/ytsage_*.deb 2>/dev/null | head -n1 || true)
|
|
if [ -n "$deb_src" ]; then
|
|
cp "$deb_src" "artifacts/YTSage-v${version}-${ARCH}.deb"
|
|
echo "Copied DEB: $deb_src -> artifacts/YTSage-v${version}-${ARCH}.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 }}
|