Build and Release #244
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Copyright 2024, MCSL Team, mailto:services@mcsl.com.cn | |
| # | |
| # Part of "MCSL2", a simple and multifunctional Minecraft server launcher. | |
| # | |
| # Licensed under the GNU General Public License, Version 3.0, with our | |
| # additional agreements. (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # https://github.com/MCSLTeam/MCSL2/raw/master/LICENSE | |
| # | |
| ################################################################################ | |
| name: Build and Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| build_target: | |
| description: Build target | |
| required: true | |
| default: all | |
| type: choice | |
| options: | |
| - windows | |
| - macos | |
| - linux | |
| - all | |
| debug_build: | |
| description: Build artifacts only, without creating a release | |
| required: true | |
| default: true | |
| type: boolean | |
| package_macos_dmg: | |
| description: Package macOS builds as DMG artifacts | |
| required: true | |
| default: true | |
| type: boolean | |
| release: | |
| types: [published] | |
| permissions: | |
| contents: write | |
| env: | |
| PYTHON_VERSION: "3.8" | |
| jobs: | |
| Windows: | |
| if: ${{ github.event_name == 'release' || github.event.inputs.build_target == 'windows' || github.event.inputs.build_target == 'all' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - architecture: x64 | |
| runner: windows-2022 | |
| experimental: false | |
| skip_arm64_missing_wheels: false | |
| use_upx: true | |
| - architecture: arm64 | |
| runner: windows-11-arm | |
| experimental: true | |
| skip_arm64_missing_wheels: true | |
| use_upx: false | |
| runs-on: ${{ matrix.runner }} | |
| continue-on-error: ${{ matrix.experimental }} | |
| steps: | |
| - name: Checkout repo | |
| uses: actions/checkout@v6.0.3 | |
| - name: Setup uv | |
| uses: astral-sh/setup-uv@v8.2.0 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: | | |
| pyproject.toml | |
| uv.lock | |
| - name: Install Python | |
| run: uv python install ${{ env.PYTHON_VERSION }} | |
| - name: Sync dependencies | |
| shell: pwsh | |
| run: uv sync --no-dev --locked ${{ matrix.skip_arm64_missing_wheels && '--no-install-package pyqt5-qt5 --no-install-package pywin32' || '' }} | |
| - name: Build with Nuitka | |
| shell: pwsh | |
| run: uv run deploy.py --disable-console | |
| - name: Optimize package | |
| shell: pwsh | |
| run: | | |
| $dist = "build/MCSL2.dist" | |
| $removePaths = @( | |
| "zstandard", | |
| "_asyncio.pyd", | |
| "pyexpat.pyd", | |
| "qt5qml.dll", | |
| "qt5qmlmodels.dll", | |
| "qt5quick.dll" | |
| ) | |
| foreach ($path in $removePaths) { | |
| Remove-Item -Force -Recurse "$dist/$path" -ErrorAction SilentlyContinue | |
| } | |
| $upxTargets = @( | |
| "_ctypes.pyd", | |
| "_decimal.pyd", | |
| "_elementtree.pyd", | |
| "_lzma.pyd", | |
| "_overlapped.pyd", | |
| "_socket.pyd", | |
| "_ssl.pyd", | |
| "_win32sysloader.pyd", | |
| "libffi-7.dll", | |
| "libcrypto-1_1.dll", | |
| "libssl-1_1.dll", | |
| "MCSL2.exe", | |
| "python38.dll", | |
| "pythoncom38.dll", | |
| "pywintypes38.dll", | |
| "qt5core.dll", | |
| "qt5dbus.dll", | |
| "qt5gui.dll", | |
| "qt5multimedia.dll", | |
| "qt5network.dll", | |
| "qt5printsupport.dll", | |
| "qt5svg.dll", | |
| "qt5websockets.dll", | |
| "qt5widgets.dll", | |
| "qt5xml.dll", | |
| "select.pyd", | |
| "unicodedata.pyd", | |
| "win32api.pyd", | |
| "win32gui.pyd", | |
| "win32print.pyd" | |
| ) | ForEach-Object { "$dist/$_" } | Where-Object { Test-Path $_ } | |
| if ("${{ matrix.use_upx }}" -eq "true" -and $upxTargets.Count -gt 0) { | |
| .\upx.exe -9 $upxTargets | |
| } | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v7.0.1 | |
| with: | |
| name: MCSL2-Windows-${{ matrix.architecture }} | |
| path: build/MCSL2.dist/**/* | |
| Linux: | |
| if: ${{ github.event_name == 'release' || github.event.inputs.build_target == 'linux' || github.event.inputs.build_target == 'all' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - architecture: x64 | |
| runner: ubuntu-22.04 | |
| skip_pyqt_qt5_wheel: false | |
| use_upx: true | |
| - architecture: arm64 | |
| runner: ubuntu-22.04-arm | |
| skip_pyqt_qt5_wheel: true | |
| use_upx: false | |
| runs-on: ${{ matrix.runner }} | |
| steps: | |
| - name: Install tools | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libfuse2 p7zip-full | |
| if [ "${{ matrix.skip_pyqt_qt5_wheel }}" = "true" ]; then | |
| sudo apt-get install -y libqt5core5a libqt5dbus5 libqt5gui5 libqt5network5 libqt5printsupport5 libqt5svg5 libqt5widgets5 libqt5xml5 qtbase5-dev qtbase5-dev-tools qt5-qmake libqt5svg5-dev qtwayland5 | |
| fi | |
| - name: Checkout repo | |
| uses: actions/checkout@v6.0.3 | |
| - name: Setup uv | |
| uses: astral-sh/setup-uv@v8.2.0 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: | | |
| pyproject.toml | |
| uv.lock | |
| - name: Install Python | |
| run: uv python install ${{ env.PYTHON_VERSION }} | |
| - name: Sync dependencies | |
| run: uv sync --no-dev --locked ${{ matrix.skip_pyqt_qt5_wheel && '--no-install-package pyqt5-qt5' || '' }} | |
| - name: Build with Nuitka | |
| run: uv run deploy.py --disable-console | |
| - name: Optimize package | |
| run: | | |
| dist="build/MCSL2.dist" | |
| rm -rf \ | |
| "$dist/zstandard" \ | |
| "$dist/_asyncio"* \ | |
| "$dist/pyexpat"* \ | |
| "$dist/libQt5Quick.so.5" \ | |
| "$dist/libQt5Qml.so.5" \ | |
| "$dist/libQt5QmlModels.so.5" | |
| if [ "${{ matrix.use_upx }}" = "true" ] && [ -f "$dist/MCSL2.bin" ]; then | |
| chmod +x ./upx | |
| ./upx -9 "$dist/MCSL2.bin" | |
| fi | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v7.0.1 | |
| with: | |
| name: MCSL2-Linux-${{ matrix.architecture }} | |
| path: build/MCSL2.dist/**/* | |
| macOS: | |
| if: ${{ github.event_name == 'release' || github.event.inputs.build_target == 'macos' || github.event.inputs.build_target == 'all' }} | |
| env: | |
| MACOS_CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE_BASE64 }} | |
| MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} | |
| MACOS_CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }} | |
| MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| PACKAGE_MACOS_DMG: ${{ github.event_name == 'release' || inputs.package_macos_dmg }} | |
| strategy: | |
| matrix: | |
| include: | |
| - architecture: arm64 | |
| runner: macos-14 | |
| - architecture: x64 | |
| runner: macos-15-intel | |
| runs-on: ${{ matrix.runner }} | |
| steps: | |
| - name: Checkout repo | |
| uses: actions/checkout@v6.0.3 | |
| - name: Setup uv | |
| uses: astral-sh/setup-uv@v8.2.0 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: | | |
| pyproject.toml | |
| uv.lock | |
| - name: Install Python | |
| run: uv python install ${{ env.PYTHON_VERSION }} | |
| - name: Sync dependencies | |
| run: uv sync --no-dev --locked | |
| - name: Build with Nuitka | |
| run: | | |
| uv run deploy.py \ | |
| --disable-console \ | |
| --macos-create-app-bundle \ | |
| --macos-app-mode=gui \ | |
| --macos-app-icon=MCSL2.icns | |
| - name: Prepare app bundle | |
| run: | | |
| app="build/MCSL2.app" | |
| exe="$app/Contents/MacOS/MCSL2" | |
| chmod +x "$exe" | |
| rm -rf "build/MCSL2.build" "build/MCSL2.dist" | |
| find "$app" -name "__pycache__" -type d -prune -exec rm -rf {} + | |
| find "$app" -name "*.pyc" -delete | |
| find "$app" -name ".DS_Store" -delete | |
| - name: Import Apple signing certificate | |
| if: >- | |
| ${{ | |
| env.MACOS_CERTIFICATE_BASE64 != '' | |
| && env.MACOS_CERTIFICATE_PASSWORD != '' | |
| && env.MACOS_CODESIGN_IDENTITY != '' | |
| && env.MACOS_KEYCHAIN_PASSWORD != '' | |
| }} | |
| run: | | |
| certificate_path="$RUNNER_TEMP/mcsl2-signing-certificate.p12" | |
| keychain_path="$RUNNER_TEMP/mcsl2-signing.keychain-db" | |
| echo "$MACOS_CERTIFICATE_BASE64" | base64 --decode > "$certificate_path" | |
| security create-keychain -p "$MACOS_KEYCHAIN_PASSWORD" "$keychain_path" | |
| security set-keychain-settings -lut 21600 "$keychain_path" | |
| security unlock-keychain -p "$MACOS_KEYCHAIN_PASSWORD" "$keychain_path" | |
| security import "$certificate_path" -P "$MACOS_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k "$keychain_path" | |
| security list-keychains -d user -s "$keychain_path" | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_KEYCHAIN_PASSWORD" "$keychain_path" | |
| - name: Sign app bundle | |
| if: >- | |
| ${{ | |
| env.MACOS_CERTIFICATE_BASE64 != '' | |
| && env.MACOS_CERTIFICATE_PASSWORD != '' | |
| && env.MACOS_CODESIGN_IDENTITY != '' | |
| && env.MACOS_KEYCHAIN_PASSWORD != '' | |
| }} | |
| run: | | |
| app="build/MCSL2.app" | |
| codesign --force --deep --timestamp --options runtime --sign "$MACOS_CODESIGN_IDENTITY" "$app" | |
| codesign --verify --deep --strict --verbose=2 "$app" | |
| - name: Notarize app bundle | |
| if: >- | |
| ${{ | |
| env.MACOS_CERTIFICATE_BASE64 != '' | |
| && env.MACOS_CERTIFICATE_PASSWORD != '' | |
| && env.MACOS_CODESIGN_IDENTITY != '' | |
| && env.MACOS_KEYCHAIN_PASSWORD != '' | |
| && env.APPLE_ID != '' | |
| && env.APPLE_TEAM_ID != '' | |
| && env.APPLE_APP_SPECIFIC_PASSWORD != '' | |
| }} | |
| run: | | |
| app="build/MCSL2.app" | |
| notarize_zip="$RUNNER_TEMP/MCSL2-${{ matrix.architecture }}-notarize.zip" | |
| ditto -c -k --keepParent "$app" "$notarize_zip" | |
| xcrun notarytool submit "$notarize_zip" \ | |
| --apple-id "$APPLE_ID" \ | |
| --team-id "$APPLE_TEAM_ID" \ | |
| --password "$APPLE_APP_SPECIFIC_PASSWORD" \ | |
| --wait | |
| xcrun stapler staple "$app" | |
| spctl --assess --type execute --verbose "$app" | |
| - name: Install DMG packaging tool | |
| if: ${{ env.PACKAGE_MACOS_DMG == 'true' }} | |
| run: npm install --global appdmg create-dmg | |
| - name: Create DMG | |
| if: ${{ env.PACKAGE_MACOS_DMG == 'true' }} | |
| run: | | |
| dmg_path="build/MCSL2-macOS-${{ matrix.architecture }}.dmg" | |
| background_path="macos-volume-bg.png" | |
| if [ -f "$background_path" ]; then | |
| cat > "$RUNNER_TEMP/mcsl2-dmg.json" <<JSON | |
| { | |
| "title": "MCSL2", | |
| "icon": "macos-volume-icon.icns", | |
| "background": "$background_path", | |
| "window": { | |
| "size": { | |
| "width": 480, | |
| "height": 540 | |
| } | |
| }, | |
| "contents": [ | |
| { | |
| "x": 240, | |
| "y": 145, | |
| "type": "file", | |
| "path": "build/MCSL2.app" | |
| }, | |
| { | |
| "x": 240, | |
| "y": 405, | |
| "type": "link", | |
| "path": "/Applications" | |
| } | |
| ] | |
| } | |
| JSON | |
| appdmg "$RUNNER_TEMP/mcsl2-dmg.json" "$dmg_path" | |
| else | |
| create-dmg build/MCSL2.app build --overwrite --no-code-sign | |
| generated_dmg="$(find build -maxdepth 1 -name '*.dmg' | head -n 1)" | |
| test -n "$generated_dmg" | |
| mv "$generated_dmg" "$dmg_path" | |
| fi | |
| - name: Notarize DMG | |
| if: >- | |
| ${{ | |
| env.PACKAGE_MACOS_DMG == 'true' | |
| && env.MACOS_CERTIFICATE_BASE64 != '' | |
| && env.MACOS_CERTIFICATE_PASSWORD != '' | |
| && env.MACOS_CODESIGN_IDENTITY != '' | |
| && env.MACOS_KEYCHAIN_PASSWORD != '' | |
| && env.APPLE_ID != '' | |
| && env.APPLE_TEAM_ID != '' | |
| && env.APPLE_APP_SPECIFIC_PASSWORD != '' | |
| }} | |
| run: | | |
| dmg_path="build/MCSL2-macOS-${{ matrix.architecture }}.dmg" | |
| xcrun notarytool submit "$dmg_path" \ | |
| --apple-id "$APPLE_ID" \ | |
| --team-id "$APPLE_TEAM_ID" \ | |
| --password "$APPLE_APP_SPECIFIC_PASSWORD" \ | |
| --wait | |
| xcrun stapler staple "$dmg_path" | |
| spctl --assess --type open --verbose "$dmg_path" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v7.0.1 | |
| with: | |
| name: MCSL2-macOS-${{ matrix.architecture }} | |
| path: | | |
| build/MCSL2.app | |
| build/MCSL2-macOS-${{ matrix.architecture }}.dmg | |
| if-no-files-found: error | |
| Release: | |
| runs-on: ubuntu-22.04 | |
| needs: | |
| - Windows | |
| - Linux | |
| - macOS | |
| if: >- | |
| ${{ | |
| always() | |
| && (github.event_name == 'release' || !inputs.debug_build) | |
| && !contains(needs.*.result, 'failure') | |
| && !contains(needs.*.result, 'cancelled') | |
| && contains(needs.*.result, 'success') | |
| }} | |
| steps: | |
| - name: Install tools | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y p7zip-full | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v8.0.1 | |
| - name: Checkout repo | |
| uses: actions/checkout@v6.0.3 | |
| with: | |
| path: repo | |
| - name: Set Environment Variable | |
| run: | | |
| cd repo | |
| mcsl2_version=$(python3 -c "from MCSL2Lib import MCSL2VERSION; print(MCSL2VERSION)") | |
| echo "MCSL2_VERSION=$mcsl2_version" >> "$GITHUB_ENV" | |
| cd .. | |
| - name: Compress release artifacts | |
| run: | | |
| shopt -s nullglob | |
| for dir in MCSL2-Windows-x64 MCSL2-Windows-arm64 MCSL2-Linux-x64 MCSL2-Linux-arm64; do | |
| if [ -d "$dir" ]; then | |
| 7z a -tzip -mx=9 "MCSL2-${MCSL2_VERSION}-${dir#MCSL2-}.zip" "./$dir/*" | |
| fi | |
| done | |
| for dir in MCSL2-macOS-arm64 MCSL2-macOS-x64; do | |
| if compgen -G "$dir/*.dmg" > /dev/null; then | |
| cp "$dir"/*.dmg . | |
| elif [ -d "$dir" ]; then | |
| 7z a -tzip -mx=9 "MCSL2-${MCSL2_VERSION}-${dir#MCSL2-}.zip" "./$dir/*" | |
| fi | |
| done | |
| - name: Create update package | |
| run: | | |
| mkdir -p Update/Windows-x64 Update/Windows-arm64 Update/Linux-x64 Update/Linux-arm64 Update/macOS-arm64 Update/macOS-x64 | |
| if [ -f MCSL2-Windows-x64/MCSL2.exe ]; then | |
| cp MCSL2-Windows-x64/MCSL2.exe Update/Windows-x64/ | |
| fi | |
| if [ -f MCSL2-Windows-arm64/MCSL2.exe ]; then | |
| cp MCSL2-Windows-arm64/MCSL2.exe Update/Windows-arm64/ | |
| fi | |
| if [ -f MCSL2-Linux-x64/MCSL2.bin ]; then | |
| cp MCSL2-Linux-x64/MCSL2.bin Update/Linux-x64/ | |
| fi | |
| if [ -f MCSL2-Linux-arm64/MCSL2.bin ]; then | |
| cp MCSL2-Linux-arm64/MCSL2.bin Update/Linux-arm64/ | |
| fi | |
| if [ -d MCSL2-macOS-arm64/MCSL2.app ]; then | |
| cp -R MCSL2-macOS-arm64/MCSL2.app Update/macOS-arm64/ | |
| fi | |
| if [ -d MCSL2-macOS-x64/MCSL2.app ]; then | |
| cp -R MCSL2-macOS-x64/MCSL2.app Update/macOS-x64/ | |
| fi | |
| for platform in Windows-x64 Windows-arm64 Linux-x64 Linux-arm64 macOS-arm64 macOS-x64; do | |
| mkdir -p "Update/$platform/MCSL2Lib" | |
| done | |
| [ -f MCSL2-Windows-x64/MCSL2Lib/verification.pyd ] \ | |
| && cp MCSL2-Windows-x64/MCSL2Lib/verification.pyd Update/Windows-x64/MCSL2Lib/ | |
| [ -f MCSL2-Windows-arm64/MCSL2Lib/verification.pyd ] \ | |
| && cp MCSL2-Windows-arm64/MCSL2Lib/verification.pyd Update/Windows-arm64/MCSL2Lib/ | |
| [ -f MCSL2-Linux-x64/MCSL2Lib/verification.so ] \ | |
| && cp MCSL2-Linux-x64/MCSL2Lib/verification.so Update/Linux-x64/MCSL2Lib/ | |
| [ -f MCSL2-Linux-arm64/MCSL2Lib/verification.so ] \ | |
| && cp MCSL2-Linux-arm64/MCSL2Lib/verification.so Update/Linux-arm64/MCSL2Lib/ | |
| find Update -type d -empty -delete | |
| 7z a -tzip -mx=9 "Update-${MCSL2_VERSION}.zip" "./Update/*" | |
| - name: Release | |
| uses: softprops/action-gh-release@v3.0.0 | |
| with: | |
| body_path: ./repo/ChangeLog.md | |
| prerelease: true | |
| draft: false | |
| tag_name: v${{ env.MCSL2_VERSION }} | |
| files: | | |
| *.zip | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |