3fbc587e02
Introduces a GitHub Actions workflow for building and packaging macOS releases. Automates version extraction, Python environment setup, dependency installation, app and DMG creation, artifact packaging, and draft release publishing for tagged commits.
222 lines
6.3 KiB
YAML
222 lines
6.3 KiB
YAML
name: Build macOS Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
env:
|
|
PYTHON_VERSION: '3.13.6'
|
|
|
|
jobs:
|
|
build-macos:
|
|
runs-on: macos-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
|
|
~/Library/Caches/pip
|
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-pip-
|
|
|
|
- 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 dmgbuild yt-dlp
|
|
|
|
- name: Prepare build variables
|
|
shell: bash
|
|
run: |
|
|
version="${{ steps.get_version.outputs.VERSION }}"
|
|
echo "VERSION=$version" >> "$GITHUB_ENV"
|
|
echo "Prepared build variables for version: $version"
|
|
|
|
- name: Create cx_Freeze setup script
|
|
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=f"YTSage-v{version}",
|
|
icon="assets/branding/icons/icon.icns",
|
|
)
|
|
]
|
|
|
|
setup(
|
|
name="YTSage",
|
|
version=version,
|
|
description="YTSage",
|
|
options={
|
|
"build_exe": build_exe_options,
|
|
"bdist_mac": {
|
|
"iconfile": "assets/branding/icons/icon.icns",
|
|
"bundle_name": f"YTSage-v{version}",
|
|
},
|
|
"bdist_dmg": {
|
|
"volume_label": f"YTSage v{version}",
|
|
"applications_shortcut": True,
|
|
# Sensible defaults; can be customized later if desired
|
|
"format": "UDZO",
|
|
"filesystem": "HFS+",
|
|
"default_view": "icon-view",
|
|
},
|
|
},
|
|
executables=executables,
|
|
)
|
|
PY
|
|
|
|
- name: Build .app bundle (bdist_mac)
|
|
shell: bash
|
|
run: |
|
|
source venv/bin/activate
|
|
python setup_cxfreeze.py bdist_mac
|
|
|
|
app_name="YTSage-v${VERSION}.app"
|
|
app_path="dist/${app_name}"
|
|
if [ -d "$app_path/Contents/Resources/assets/branding/screenshots" ]; then
|
|
rm -rf "$app_path/Contents/Resources/assets/branding/screenshots"
|
|
echo "Removed screenshots folder from .app bundle"
|
|
fi
|
|
|
|
- name: Build DMG (bdist_dmg)
|
|
shell: bash
|
|
run: |
|
|
source venv/bin/activate
|
|
python setup_cxfreeze.py bdist_dmg
|
|
|
|
- name: Package and prepare release artifacts (.app.zip and .dmg)
|
|
shell: bash
|
|
run: |
|
|
version="${{ steps.get_version.outputs.VERSION }}"
|
|
echo "Preparing artifacts for version: $version"
|
|
mkdir -p artifacts
|
|
|
|
app_name="YTSage-v${version}.app"
|
|
app_path="dist/${app_name}"
|
|
if [ -d "$app_path" ]; then
|
|
(cd dist && zip -r "../artifacts/YTSage-v${version}.app.zip" "$app_name")
|
|
echo "Created: $(pwd)/artifacts/YTSage-v${version}.app.zip"
|
|
else
|
|
echo "Warning: .app bundle not found at $app_path"
|
|
fi
|
|
|
|
# Try to find the generated DMG in dist
|
|
dmg_src=""
|
|
if [ -f "dist/YTSage-v${version}.dmg" ]; then
|
|
dmg_src="dist/YTSage-v${version}.dmg"
|
|
else
|
|
# Fallback: pick first .dmg in dist
|
|
dmg_src=$(ls dist/*.dmg 2>/dev/null | head -n 1 || true)
|
|
fi
|
|
|
|
if [ -n "$dmg_src" ] && [ -f "$dmg_src" ]; then
|
|
cp "$dmg_src" "artifacts/YTSage-v${version}.dmg"
|
|
echo "Copied DMG to artifacts: artifacts/YTSage-v${version}.dmg"
|
|
else
|
|
echo "Warning: No DMG found in dist"
|
|
fi
|
|
|
|
echo "Final artifacts:"
|
|
ls -lh artifacts || true
|
|
|
|
- name: Create draft release
|
|
uses: softprops/action-gh-release@v1
|
|
with:
|
|
tag_name: ${{ github.ref_name }}
|
|
name: YTSage ${{ github.ref_name }}
|
|
draft: true
|
|
prerelease: 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 }}
|