name: Build Executables (Test) on: workflow_dispatch: permissions: contents: read env: PYTHON_VERSION: "3.12" APP_NAME: "SoftwareManager" jobs: build: name: Build ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: ubuntu-22.04 artifact_name: SoftwareManager-test-${{ github.sha }}-linux - os: windows-latest artifact_name: SoftwareManager-test-${{ github.sha }}-windows - os: macos-latest artifact_name: SoftwareManager-test-${{ github.sha }}-macos runs-on: ${{ matrix.os }} steps: - name: Checkout code uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v7 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install uv uses: astral-sh/setup-uv@v7 with: version: "0.9.7" enable-cache: true - name: Install system dependencies (Linux) if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y \ libxcb-xinerama0 \ libxkbcommon-x11-0 \ libxcb-cursor0 \ libxcb-keysyms1 \ libxcb-render-util0 \ libxcb-icccm4 \ libxcb-image0 \ libxcb-shape0 \ libxcb-util1 - name: Install Python dependencies run: | uv pip install -r requirements.txt uv pip install Pillow env: UV_SYSTEM_PYTHON: 1 - name: Install Nuitka (Windows only) if: runner.os == 'Windows' run: uv pip install nuitka env: UV_SYSTEM_PYTHON: 1 - name: Generate version id: version shell: bash run: | SHORT_SHA="${{ github.sha }}" SHORT_SHA="${SHORT_SHA:0:7}" BRANCH="${{ github.head_ref || github.ref_name }}" BRANCH_SLUG="${BRANCH//\//-}" # replace slashes with dashes VERSION="${BRANCH_SLUG}-${SHORT_SHA}-test" echo "VERSION=$VERSION" >> $GITHUB_ENV echo "version=$VERSION" >> $GITHUB_OUTPUT echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT echo "Generated version: $VERSION" - name: Inject build info shell: bash run: | cat > build_info.json << EOF { "version": "${{ env.VERSION }}" } EOF - name: Install UPX (Windows) if: runner.os == 'Windows' shell: bash run: | curl -sL https://github.com/upx/upx/releases/download/v5.1.0/upx-5.1.0-win64.zip -o upx.zip 7z x upx.zip -oupx-dir UPX_DIR="$(pwd)/upx-dir/upx-5.1.0-win64" echo "$UPX_DIR" >> $GITHUB_PATH export PATH="$UPX_DIR:$PATH" echo "UPX_BINARY=$UPX_DIR/upx.exe" >> $GITHUB_ENV upx --version | head -1 - name: Convert icon for Windows if: runner.os == 'Windows' shell: bash run: | python -c " from PIL import Image img = Image.open('src/interface/assets/logo.png') img.save('app_icon.ico', format='ICO', sizes=[(256,256),(128,128),(64,64),(48,48),(32,32),(16,16)]) print('Icon converted successfully') " - name: Build with Nuitka (Windows) if: runner.os == 'Windows' shell: bash run: | python -m nuitka \ --standalone \ --assume-yes-for-downloads \ --windows-console-mode=disable \ --windows-icon-from-ico=app_icon.ico \ --enable-plugin=pyside6 \ --enable-plugin=upx \ --upx-binary="${{ env.UPX_BINARY }}" \ --noinclude-qt-translations \ --include-data-files=build_info.json=build_info.json \ --output-filename=${{ env.APP_NAME }}.exe \ --output-dir=nuitka-build \ src/main.py - name: Build with PyInstaller (Linux) if: runner.os == 'Linux' shell: bash run: | pyinstaller --onefile --windowed \ --add-data "build_info.json:." \ --exclude-module PyQt6 \ --exclude-module PyQt5 \ --hidden-import=backports.tarfile \ --hidden-import=backports \ --hidden-import=jaraco.context \ --hidden-import=jaraco.text \ --hidden-import=jaraco.functools \ --name ${{ env.APP_NAME }} \ src/main.py - name: Build with PyInstaller (macOS) if: runner.os == 'macOS' shell: bash run: | pyinstaller --windowed \ --add-data "build_info.json:." \ --exclude-module PyQt6 \ --exclude-module PyQt5 \ --hidden-import=backports.tarfile \ --hidden-import=backports \ --hidden-import=jaraco.context \ --hidden-import=jaraco.text \ --hidden-import=jaraco.functools \ --name ${{ env.APP_NAME }} \ src/main.py - name: Install create-dmg (macOS) if: runner.os == 'macOS' run: brew install create-dmg - name: Stage artifact shell: bash run: | mkdir -p dist-final if [ "$RUNNER_OS" == "Windows" ]; then mv nuitka-build/main.dist dist-final/${{ env.APP_NAME }} elif [ "$RUNNER_OS" == "Linux" ]; then chmod +x dist/${{ env.APP_NAME }} mv dist/${{ env.APP_NAME }} dist-final/${{ matrix.artifact_name }} elif [ "$RUNNER_OS" == "macOS" ]; then create-dmg \ --volname "${{ env.APP_NAME }} Test" \ --window-pos 200 120 \ --window-size 600 300 \ --icon-size 100 \ --icon "${{ env.APP_NAME }}.app" 150 150 \ --hide-extension "${{ env.APP_NAME }}.app" \ --app-drop-link 450 150 \ "dist-final/${{ matrix.artifact_name }}.dmg" \ "dist/" fi ls -la dist-final/ - name: Create ZIP (Windows) if: runner.os == 'Windows' shell: pwsh run: | Compress-Archive -Path "dist-final/${{ env.APP_NAME }}/*" -DestinationPath "dist-final/${{ matrix.artifact_name }}.zip" - name: Upload artifact (Windows) if: runner.os == 'Windows' uses: actions/upload-artifact@v7 with: name: ${{ matrix.os }}-build path: dist-final/${{ matrix.artifact_name }}.zip retention-days: 7 - name: Upload artifact (Linux/macOS) if: runner.os != 'Windows' uses: actions/upload-artifact@v7 with: name: ${{ matrix.os }}-build path: dist-final/${{ matrix.artifact_name }}* retention-days: 7