Skip to content

chore(release): 0.0.51 #156

chore(release): 0.0.51

chore(release): 0.0.51 #156

Workflow file for this run

name: Build Multi-Platform Release
on:
push:
tags:
- 'v*'
branches:
- master
pull_request:
branches:
- master
workflow_dispatch:
inputs:
create_release:
description: 'Create GitHub Release'
required: false
default: false
type: boolean
env:
PYTHON_VERSION: '3.11'
QDC_QUIET: '1'
jobs:
build:
name: Build ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# macOS Intel (macos-13 retired, using macos-15 with x64 arch)
- os: macos-15
platform: macos-intel
arch: x86_64
runner_arch: x64
# macOS Apple Silicon
- os: macos-14
platform: macos-arm64
arch: arm64
runner_arch: arm64
# Linux x86_64
- os: ubuntu-latest
platform: linux
arch: x86_64
runner_arch: x64
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: ${{ matrix.runner_arch }}
- name: Install system dependencies (Linux)
if: matrix.platform == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y \
python3-dev \
build-essential \
libgl1-mesa-dev \
libxkbcommon-x11-0 \
libxcb-cursor0 \
libxcb-icccm4 \
libxcb-image0 \
libxcb-keysyms1 \
libxcb-randr0 \
libxcb-render-util0 \
libxcb-xinerama0 \
libxcb-xfixes0 \
libxcb-shape0 \
libxcb-sync1 \
libxcb-shm0 \
libxcb-render0 \
libxcb-util1 \
libxcb-glx0 \
libxcb-dri2-0 \
libxcb-dri3-0 \
libxcb-present0 \
libxcb1 \
libxcb-xkb1 \
libxcb-xinput0 \
libfontconfig1 \
libfreetype6 \
libx11-6 \
libx11-xcb1 \
libxi6 \
libxrender1 \
libdbus-1-3 \
libegl1 \
libxcb-xtest0
- name: Install system dependencies (macOS)
if: startsWith(matrix.platform, 'macos')
run: |
# Install Homebrew if not present (for self-hosted runners)
if ! command -v brew &> /dev/null; then
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
# Update Homebrew
brew update
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Install Python dependencies
run: uv sync
- name: Verify PyQt6 installation
run: |
uv run python -c "from PyQt6 import QtCore; print(f'PyQt6 version: {QtCore.PYQT_VERSION_STR}')"
uv run python -c "from PyQt6 import QtWidgets; print('PyQt6.QtWidgets imported successfully')"
- name: Set up display (Linux)
if: matrix.platform == 'linux'
run: |
# Set up virtual display for GUI applications
export DISPLAY=:99
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
sleep 3
echo "DISPLAY=:99" >> $GITHUB_ENV
- name: Test application startup (quick smoke test)
env:
QT_QPA_PLATFORM: ${{ matrix.platform == 'linux' && 'offscreen' || '' }}
DISPLAY: ${{ matrix.platform == 'linux' && ':99' || '' }}
run: |
uv run python - <<'PY'
import os
import subprocess
import sys
cmd = [sys.executable, "lazy_blacktea_pyqt.py", "--version"]
env = os.environ.copy()
try:
result = subprocess.run(cmd, check=False, timeout=30, env=env)
print(f"Command exited with return code {result.returncode}")
except subprocess.TimeoutExpired:
print("Command timed out after 30 seconds")
PY
- name: Clean previous builds
run: |
rm -rf build/ dist/ __pycache__/
find . -name "*.pyc" -delete
- name: Build application
run: |
echo "Starting build for platform: ${{ matrix.platform }}"
echo "Architecture: ${{ matrix.arch }}"
echo "Working directory: $(pwd)"
uv run python build-scripts/build.py
echo "Build script completed with exit code: $?"
echo "Contents of dist/ after build:"
ls -la dist/ || echo "No dist directory created"
env:
# Ensure we're building for the correct architecture
ARCHFLAGS: ${{ matrix.platform == 'macos-arm64' && '-arch arm64' || matrix.platform == 'macos-intel' && '-arch x86_64' || '' }}
CFLAGS: ${{ matrix.platform == 'macos-arm64' && '-arch arm64' || matrix.platform == 'macos-intel' && '-arch x86_64' || '' }}
LDFLAGS: ${{ matrix.platform == 'macos-arm64' && '-arch arm64' || matrix.platform == 'macos-intel' && '-arch x86_64' || '' }}
- name: Verify build artifacts
run: |
ls -la dist/
if [ "${{ matrix.platform }}" = "linux" ]; then
# Verify Linux build
[ -d "dist/lazyblacktea" ] && echo "✅ Linux executable found" || (echo "❌ Linux executable missing" && exit 1)
[ -f "dist/lazyblacktea-linux.tar.gz" ] && echo "✅ Linux tarball found" || echo "⚠️ Linux tarball missing"
[ -f "dist/LazyBlacktea-x86_64.AppImage" ] && echo "✅ AppImage found" || echo "⚠️ AppImage missing"
else
# Verify macOS build
[ -d "dist/LazyBlacktea.app" ] && echo "✅ macOS app bundle found" || (echo "❌ macOS app bundle missing" && exit 1)
[ -f "dist/LazyBlacktea.dmg" ] && echo "✅ macOS DMG found" || echo "⚠️ macOS DMG missing"
fi
- name: Test built application (quick smoke test)
env:
QT_QPA_PLATFORM: ${{ matrix.platform == 'linux' && 'offscreen' || '' }}
DISPLAY: ${{ matrix.platform == 'linux' && ':99' || '' }}
MATRIX_PLATFORM: ${{ matrix.platform }}
run: |
uv run python - <<'PY'
import os
import subprocess
platform = os.environ["MATRIX_PLATFORM"]
target = "./dist/lazyblacktea/lazyblacktea" if platform == "linux" else "./dist/LazyBlacktea.app/Contents/MacOS/LazyBlacktea"
env = os.environ.copy()
try:
result = subprocess.run([target, "--version"], check=False, timeout=30, env=env)
print(f"{target} exited with return code {result.returncode}")
except FileNotFoundError as err:
print(f"Executable not found: {err}")
except subprocess.TimeoutExpired:
print("Command timed out after 30 seconds")
PY
- name: Prepare artifacts
run: |
mkdir -p artifacts
echo "Platform: ${{ matrix.platform }}"
echo "Architecture: ${{ matrix.arch }}"
echo "Available files in dist/:"
ls -la dist/ || echo "No dist directory found"
if [ "${{ matrix.platform }}" = "linux" ]; then
# Copy Linux artifacts
echo "Looking for Linux artifacts..."
if [ -f "dist/lazyblacktea-linux.tar.gz" ]; then
cp "dist/lazyblacktea-linux.tar.gz" "artifacts/lazyblacktea-linux-${{ matrix.arch }}.tar.gz"
echo "✅ Copied tarball"
else
echo "⚠️ Tarball not found"
fi
if [ -f "dist/LazyBlacktea-x86_64.AppImage" ]; then
cp "dist/LazyBlacktea-x86_64.AppImage" "artifacts/LazyBlacktea-linux-${{ matrix.arch }}.AppImage"
echo "✅ Copied AppImage"
else
echo "⚠️ AppImage not found"
fi
else
# Copy macOS artifacts
echo "Looking for macOS artifacts..."
if [ -f "dist/LazyBlacktea.dmg" ]; then
cp "dist/LazyBlacktea.dmg" "artifacts/LazyBlacktea-${{ matrix.platform }}.dmg"
echo "✅ Copied DMG"
else
echo "⚠️ DMG not found"
fi
# Also create a zip of the app bundle as fallback
if [ -d "dist/LazyBlacktea.app" ]; then
cd dist && zip -r "../artifacts/LazyBlacktea-${{ matrix.platform }}.zip" LazyBlacktea.app && cd ..
echo "✅ Created ZIP from app bundle"
else
echo "⚠️ App bundle not found"
fi
fi
echo "Final artifacts:"
ls -la artifacts/ || echo "No artifacts created"
# Ensure we have at least one file to upload
if [ ! "$(ls -A artifacts/)" ]; then
echo "No artifacts were created. Creating a build report..."
echo "Build completed but no artifacts were generated." > artifacts/build-report-${{ matrix.platform }}.txt
echo "Platform: ${{ matrix.platform }}" >> artifacts/build-report-${{ matrix.platform }}.txt
echo "Architecture: ${{ matrix.arch }}" >> artifacts/build-report-${{ matrix.platform }}.txt
echo "Build time: $(date)" >> artifacts/build-report-${{ matrix.platform }}.txt
fi
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.platform }}
path: artifacts/*
retention-days: 30
if-no-files-found: warn
- name: Upload build logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: build-logs-${{ matrix.platform }}-${{ github.run_number }}
path: |
build/
*.log
retention-days: 7
if-no-files-found: ignore
release:
name: Create Release
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && github.event.inputs.create_release == 'true')
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/
pattern: build-*
merge-multiple: true
- name: Prepare release assets
run: |
mkdir -p release-assets
# Copy all build artifacts to release assets
find artifacts/ -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.tar.gz" -o -name "*.AppImage" \) -exec cp {} release-assets/ \;
# List all release assets
echo "Release assets:"
ls -la release-assets/
- name: Generate release notes
id: release_notes
run: |
set -euo pipefail
if [[ "${{ github.ref }}" =~ refs/tags/(v.+) ]]; then
VERSION="${BASH_REMATCH[1]}"
else
VERSION="development-$(date +%Y%m%d-%H%M%S)"
fi
git fetch --tags --force >/dev/null 2>&1 || true
CURRENT_TAG=""
if git describe --tags --exact-match >/dev/null 2>&1; then
CURRENT_TAG="$(git describe --tags --exact-match)"
fi
PREVIOUS_TAG=""
if [[ -n "${CURRENT_TAG}" ]]; then
PREVIOUS_TAG="$(git describe --tags --abbrev=0 "${CURRENT_TAG}^" 2>/dev/null || true)"
else
PREVIOUS_TAG="$(git describe --tags --abbrev=0 2>/dev/null || true)"
fi
if [[ -n "${PREVIOUS_TAG}" ]]; then
CHANGELOG="$(git log --no-merges --pretty=format:'- %s (%h)' "${PREVIOUS_TAG}"..HEAD)"
else
CHANGELOG="$(git log --no-merges --pretty=format:'- %s (%h)')"
fi
if [[ -z "${CHANGELOG}" ]]; then
CHANGELOG='- No code changes detected.'
fi
cat > release-notes.md <<EOF
# Lazy Blacktea ${VERSION}
## 📦 Downloads
### macOS
- **macOS Intel (x86_64)**: \`LazyBlacktea-macos-intel.dmg\` or \`LazyBlacktea-macos-intel.zip\`
- **macOS Apple Silicon (ARM64)**: \`LazyBlacktea-macos-arm64.dmg\` or \`LazyBlacktea-macos-arm64.zip\`
### Linux
- **Linux x86_64**: \`lazyblacktea-linux-x86_64.tar.gz\` or \`LazyBlacktea-linux-x86_64.AppImage\`
## 🚀 Installation
### macOS
1. Download the appropriate \`.dmg\` file for your Mac (Intel or Apple Silicon)
2. Open the \`.dmg\` file and drag Lazy Blacktea to Applications
3. **Important**: macOS will show security warnings on first launch
#### If you see "Cannot verify the app is free of malware":
**Method 1 - Right-click to Open:**
- Right-click on LazyBlacktea.app
- Select "Open" from context menu
- Click "Open" again when prompted
**Method 2 - System Preferences:**
- Try to open the app normally (it will be blocked)
- Go to System Preferences → Security & Privacy → General
- Click "Open Anyway" button next to the LazyBlacktea message
**Method 3 - Terminal (if app crashes immediately):**
```bash
# Remove quarantine and re-sign the app
xattr -rd com.apple.quarantine /Applications/LazyBlacktea.app
codesign --force --deep --sign - /Applications/LazyBlacktea.app
```
> **Note**: The app is signed with ad-hoc signature but not notarized by Apple. This is safe to use - it's just not distributed through the App Store.
### Linux
- **AppImage**: Download and run \`chmod +x LazyBlacktea-linux-x86_64.AppImage && ./LazyBlacktea-linux-x86_64.AppImage\`
- **Tarball**: Extract and run \`tar -xzf lazyblacktea-linux-x86_64.tar.gz && ./lazyblacktea/lazy_blacktea\`
## 📝 Changes
${CHANGELOG}
Built from commit: \`${{ github.sha }}\`
## 🔒 Security Note & Troubleshooting
**macOS Apps**: These applications are signed with ad-hoc signatures for security but are not notarized by Apple.
### Common Issues:
1. **"Cannot verify the app is free of malware"** - Use methods above ✅
> **Latest builds (v0.0.6+)**: Include improved signing and should work without manual intervention.
**Fixed Issues (v0.0.6+)**:
- ✅ **App immediate closure fixed**: Previous versions crashed due to ADB path detection issues
- ✅ **Improved code signing**: Apps now properly signed during build process
- ✅ **Better dependency detection**: PyInstaller spec includes all required modules
### Why security warnings occur:
- ✅ The apps are properly signed and safe to use
- ⚠️ macOS Gatekeeper shows warnings for apps not distributed through App Store
- 💡 Our automated build process includes proper signing to prevent crashes
For production releases, consider upgrading to Apple Developer Program for full notarization.
---
**Note**: This is an automated release built with GitHub Actions.
EOF
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || format('v{0}', steps.release_notes.outputs.VERSION) }}
name: Lazy Blacktea ${{ steps.release_notes.outputs.VERSION }}
body_path: release-notes.md
files: release-assets/*
draft: false
prerelease: ${{ contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') || contains(github.ref_name, 'rc') }}
token: ${{ secrets.GITHUB_TOKEN }}
allowUpdates: true
notify:
name: Build Status Notification
needs: [build]
runs-on: ubuntu-latest
if: always()
steps:
- name: Report build status
run: |
if [ "${{ needs.build.result }}" = "success" ]; then
echo "✅ All builds completed successfully!"
else
echo "❌ Some builds failed. Check the build logs for details."
exit 1
fi