Files
SageTube/.github/workflows/build-linux.yml
T
Your Name 1b552c4ae8 Improve DEB package extraction and repackaging logic
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.
2025-09-02 19:47:28 +03:00

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 }}