name: Create Release on: workflow_dispatch: permissions: contents: write env: PYTHON_VERSION: "3.12" APP_NAME: "SoftwareManager" jobs: build: name: Build ${{ matrix.os }} strategy: matrix: include: - os: ubuntu-22.04 artifact_name: SoftwareManager-stable-${{ github.sha }}-linux asset_name: SoftwareManager-stable-${{ github.sha }}-linux - os: windows-latest artifact_name: SoftwareManager-stable-${{ github.sha }}-windows asset_name: SoftwareManager-stable-${{ github.sha }}-windows - os: macos-latest artifact_name: SoftwareManager-stable-${{ github.sha }}-macos asset_name: SoftwareManager-stable-${{ github.sha }}-macos runs-on: ${{ matrix.os }} outputs: version: ${{ steps.version.outputs.version }} short_sha: ${{ steps.version.outputs.short_sha }} 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: | COMMIT_SHA=${{ github.sha }} SHORT_SHA=${COMMIT_SHA:0:7} VERSION="${SHORT_SHA}-stable" echo "VERSION=$VERSION" >> $GITHUB_ENV echo "version=$VERSION" >> $GITHUB_OUTPUT echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT echo "Generated version: $VERSION" - name: Inject commit hash into code 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: Rename artifact shell: bash run: | mkdir -p dist-final if [ "$RUNNER_OS" == "Windows" ]; then mv nuitka-build/main.dist dist-final/${{ env.APP_NAME }} ls -la 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 }}" \ --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" Write-Host "Created ZIP: ${{ matrix.artifact_name }}.zip" - name: Build Installer (Windows) if: runner.os == 'Windows' shell: bash run: | SHORT_SHA="${{ github.sha }}" SHORT_SHA="${SHORT_SHA:0:7}" SETUP_NAME="SoftwareManager-stable-${SHORT_SHA}-windows-setup" iscc //DMyAppVersion="${{ env.VERSION }}" \ //DMySourceDir="dist-final\\${{ env.APP_NAME }}" \ //DMyOutputDir="dist-final" \ //DMyOutputFilename="${SETUP_NAME}" \ installer.iss echo "SETUP_NAME=${SETUP_NAME}" >> $GITHUB_ENV ls -la dist-final/ - name: Upload build artifact (Windows) if: runner.os == 'Windows' uses: actions/upload-artifact@v7 with: name: ${{ matrix.os }}-build path: | dist-final/${{ matrix.artifact_name }}.zip dist-final/${{ env.SETUP_NAME }}.exe retention-days: 1 - name: Upload build 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: 1 release: name: Create Release needs: build runs-on: ubuntu-22.04 if: success() steps: - uses: actions/checkout@v6 - uses: actions/download-artifact@v8 with: path: artifacts - name: Prepare release files run: | mkdir -p release SHORT_SHA="${{ needs.build.outputs.short_sha }}" cp artifacts/ubuntu-22.04-build/SoftwareManager-stable-*-linux release/SoftwareManager-stable-${SHORT_SHA}-linux cp artifacts/windows-latest-build/SoftwareManager-stable-*-windows.zip release/SoftwareManager-stable-${SHORT_SHA}-windows.zip cp artifacts/windows-latest-build/SoftwareManager-stable-*-windows-setup.exe release/SoftwareManager-stable-${SHORT_SHA}-windows-setup.exe cp artifacts/macos-latest-build/SoftwareManager-stable-*-macos.dmg release/SoftwareManager-stable-${SHORT_SHA}-macos.dmg chmod +x release/SoftwareManager-stable-*-linux ls -la release/ - name: Generate checksums run: | cd release sha256sum * > SHA256SUMS.txt cat SHA256SUMS.txt - name: Create Release (Public) uses: softprops/action-gh-release@v3 with: token: ${{ secrets.RELEASE_TOKEN }} tag_name: ${{ needs.build.outputs.version }} name: ${{ needs.build.outputs.version }} generate_release_notes: true files: | release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-linux release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-windows.zip release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-windows-setup.exe release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-macos.dmg release/SHA256SUMS.txt