diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c9721e0..705bff5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,39 +90,17 @@ jobs: - name: Create macOS icon from ICO run: | - python3 -c " - from PIL import Image - img = Image.open('icon.ico') - img = img.resize((1024, 1024), Image.LANCZOS) - img.save('icon_1024.png', 'PNG') - " - mkdir -p icon.iconset - sips -z 16 16 icon_1024.png --out icon.iconset/icon_16x16.png - sips -z 32 32 icon_1024.png --out icon.iconset/icon_16x16@2x.png - sips -z 32 32 icon_1024.png --out icon.iconset/icon_32x32.png - sips -z 64 64 icon_1024.png --out icon.iconset/icon_32x32@2x.png - sips -z 128 128 icon_1024.png --out icon.iconset/icon_128x128.png - sips -z 256 256 icon_1024.png --out icon.iconset/icon_128x128@2x.png - sips -z 256 256 icon_1024.png --out icon.iconset/icon_256x256.png - sips -z 512 512 icon_1024.png --out icon.iconset/icon_256x256@2x.png - sips -z 512 512 icon_1024.png --out icon.iconset/icon_512x512.png - sips -z 1024 1024 icon_1024.png --out icon.iconset/icon_512x512@2x.png - iconutil -c icns icon.iconset -o icon.icns - rm -rf icon.iconset icon_1024.png + chmod +x packaging/create_icon.sh + packaging/create_icon.sh - name: Build app with PyInstaller run: pyinstaller packaging/macos.spec --noconfirm - - name: Create DMG - run: | - chmod +x packaging/create_dmg.sh - packaging/create_dmg.sh - - - name: Upload artifact + - name: Upload app bundle uses: actions/upload-artifact@v4 with: - name: TgWsProxy-macOS - path: dist/TgWsProxy.dmg + name: TgWsProxy-app-arm64 + path: "dist/TG WS Proxy.app" build-macos-intel: runs-on: macos-15-intel @@ -144,43 +122,53 @@ jobs: - name: Create macOS icon from ICO run: | - python3 -c " - from PIL import Image - img = Image.open('icon.ico') - img = img.resize((1024, 1024), Image.LANCZOS) - img.save('icon_1024.png', 'PNG') - " - mkdir -p icon.iconset - sips -z 16 16 icon_1024.png --out icon.iconset/icon_16x16.png - sips -z 32 32 icon_1024.png --out icon.iconset/icon_16x16@2x.png - sips -z 32 32 icon_1024.png --out icon.iconset/icon_32x32.png - sips -z 64 64 icon_1024.png --out icon.iconset/icon_32x32@2x.png - sips -z 128 128 icon_1024.png --out icon.iconset/icon_128x128.png - sips -z 256 256 icon_1024.png --out icon.iconset/icon_128x128@2x.png - sips -z 256 256 icon_1024.png --out icon.iconset/icon_256x256.png - sips -z 512 512 icon_1024.png --out icon.iconset/icon_256x256@2x.png - sips -z 512 512 icon_1024.png --out icon.iconset/icon_512x512.png - sips -z 1024 1024 icon_1024.png --out icon.iconset/icon_512x512@2x.png - iconutil -c icns icon.iconset -o icon.icns - rm -rf icon.iconset icon_1024.png + chmod +x packaging/create_icon.sh + packaging/create_icon.sh - name: Build app with PyInstaller run: pyinstaller packaging/macos.spec --noconfirm - - name: Create DMG + - name: Upload app bundle + uses: actions/upload-artifact@v4 + with: + name: TgWsProxy-app-x86_64 + path: "dist/TG WS Proxy.app" + + build-macos-universal: + needs: [build-macos, build-macos-intel] + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download arm64 app + uses: actions/download-artifact@v4 + with: + name: TgWsProxy-app-arm64 + path: "dist/arm64/TG WS Proxy.app" + + - name: Download x86_64 app + uses: actions/download-artifact@v4 + with: + name: TgWsProxy-app-x86_64 + path: "dist/x86_64/TG WS Proxy.app" + + - name: Merge into universal2 app and create DMG run: | - chmod +x packaging/create_dmg.sh - packaging/create_dmg.sh - mv dist/TgWsProxy.dmg dist/TgWsProxy-Intel.dmg + chmod +x packaging/merge_universal2.sh + packaging/merge_universal2.sh \ + "dist/arm64/TG WS Proxy.app" \ + "dist/x86_64/TG WS Proxy.app" \ + "dist/TG WS Proxy.app" - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: TgWsProxy-macOS-Intel - path: dist/TgWsProxy-Intel.dmg + name: TgWsProxy-macOS + path: dist/TgWsProxy.dmg release: - needs: [build, build-win7, build-macos, build-macos-intel] + needs: [build, build-win7, build-macos-universal] runs-on: ubuntu-latest steps: - name: Download main build @@ -201,12 +189,6 @@ jobs: name: TgWsProxy-macOS path: dist - - name: Download macOS Intel build - uses: actions/download-artifact@v4 - with: - name: TgWsProxy-macOS-Intel - path: dist - - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: @@ -218,7 +200,6 @@ jobs: dist/TgWsProxy.exe dist/TgWsProxy-win7.exe dist/TgWsProxy.dmg - dist/TgWsProxy-Intel.dmg draft: false prerelease: false env: diff --git a/.gitignore b/.gitignore index 42a354f..8aee6ca 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ scan_ips.py scan.txt AyuGramDesktop-dev/ tweb-master/ +/icon.icns diff --git a/README.md b/README.md index 1e5dfe6..8cefc59 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,11 @@ python windows.py ### MacOS (Tray-приложение) -1. Скачать последний релиз .dmg -2. Открыть образ и перенести файл `tg_ws_proxy` в папку `Applications` +Перейдите на [страницу релизов](https://github.com/Flowseal/tg-ws-proxy/releases) и скачайте **`TgWsProxy.dmg`** — универсальная сборка для Apple Silicon и Intel. + +1. Открыть образ +2. Перенести **TG WS Proxy.app** в папку **Applications** +3. При первом запуске macOS может попросить подтвердить открытие: **Системные настройки → Конфиденциальность и безопасность → Всё равно открыть** ### Консольный режим (Windows) @@ -128,7 +131,7 @@ Tray-приложение хранит данные в `%APPDATA%/TgWsProxy`: } ``` -## Автоматическая сборка () +## Автоматическая сборка Проект содержит спецификацию PyInstaller ([`windows.spec`](packaging/windows.spec)) и GitHub Actions workflow ([`.github/workflows/build.yml`](.github/workflows/build.yml)) для автоматической сборки. @@ -142,13 +145,22 @@ pyinstaller packaging/windows.spec > Для MacOS: ```bash pip install pyinstaller +packaging/create_icon.sh pyinstaller packaging/macos.spec ``` -### Создание DMG-образа (MacOS) +> Создать универсальный DMG (local-тест с двумя копями одной архитектуры): ```bash -/bin/bash /Users/xily/PycharmProjects/tg-ws-proxy/packaging/create_dmg.sh +pip install pyinstaller +packaging/create_icon.sh +pyinstaller packaging/macos.spec --noconfirm +cp -R "dist/TG WS Proxy.app" "dist/TG WS Proxy-intel.app" +packaging/merge_universal2.sh \ + "dist/TG WS Proxy.app" \ + "dist/TG WS Proxy-intel.app" \ + "dist/TG WS Proxy-universal.app" ``` +- В результате в папке `dist/` появятся `.app` под текущую архитектуру и `TgWsProxy.dmg`. ## Лицензия diff --git a/icon_1024.png b/icon_1024.png deleted file mode 100644 index 1d6afa6..0000000 Binary files a/icon_1024.png and /dev/null differ diff --git a/packaging/create_dmg.sh b/packaging/create_dmg.sh deleted file mode 100755 index c4c341f..0000000 --- a/packaging/create_dmg.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# Create a DMG installer for TG WS Proxy macOS app -set -e - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -DIST_DIR="$PROJECT_DIR/dist" - -APP_NAME="TG WS Proxy" -DMG_NAME="TgWsProxy" - -if [ ! -d "$DIST_DIR/$APP_NAME.app" ]; then - echo "Error: $DIST_DIR/$APP_NAME.app not found" - echo "Build the app first:" - echo " pyinstaller packaging/macos.spec --noconfirm" - exit 1 -fi - -# Create temp dir for DMG contents -DMG_TEMP="$DIST_DIR/dmg_temp" -rm -rf "$DMG_TEMP" -mkdir -p "$DMG_TEMP" - -# Copy app bundle -cp -R "$DIST_DIR/$APP_NAME.app" "$DMG_TEMP/" - -# Create symlink to /Applications for drag-and-drop install -ln -s /Applications "$DMG_TEMP/Applications" - -# Create DMG -hdiutil create \ - -volname "$APP_NAME" \ - -srcfolder "$DMG_TEMP" \ - -ov \ - -format UDZO \ - "$DIST_DIR/$DMG_NAME.dmg" - -# Cleanup -rm -rf "$DMG_TEMP" - -echo "" -echo "DMG created: $DIST_DIR/$DMG_NAME.dmg" diff --git a/packaging/create_icon.sh b/packaging/create_icon.sh new file mode 100644 index 0000000..e222e7a --- /dev/null +++ b/packaging/create_icon.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Create icon.icns from icon.ico for macOS app +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +python3 -c " +from PIL import Image +img = Image.open('$PROJECT_DIR/icon.ico') +img = img.resize((1024, 1024), Image.LANCZOS) +img.save('$PROJECT_DIR/icon_1024.png', 'PNG') +" + +mkdir -p "$PROJECT_DIR/icon.iconset" +sips -z 16 16 "$PROJECT_DIR/icon_1024.png" --out "$PROJECT_DIR/icon.iconset/icon_16x16.png" +sips -z 32 32 "$PROJECT_DIR/icon_1024.png" --out "$PROJECT_DIR/icon.iconset/icon_16x16@2x.png" +sips -z 32 32 "$PROJECT_DIR/icon_1024.png" --out "$PROJECT_DIR/icon.iconset/icon_32x32.png" +sips -z 64 64 "$PROJECT_DIR/icon_1024.png" --out "$PROJECT_DIR/icon.iconset/icon_32x32@2x.png" +sips -z 128 128 "$PROJECT_DIR/icon_1024.png" --out "$PROJECT_DIR/icon.iconset/icon_128x128.png" +sips -z 256 256 "$PROJECT_DIR/icon_1024.png" --out "$PROJECT_DIR/icon.iconset/icon_128x128@2x.png" +sips -z 256 256 "$PROJECT_DIR/icon_1024.png" --out "$PROJECT_DIR/icon.iconset/icon_256x256.png" +sips -z 512 512 "$PROJECT_DIR/icon_1024.png" --out "$PROJECT_DIR/icon.iconset/icon_256x256@2x.png" +sips -z 512 512 "$PROJECT_DIR/icon_1024.png" --out "$PROJECT_DIR/icon.iconset/icon_512x512.png" +sips -z 1024 1024 "$PROJECT_DIR/icon_1024.png" --out "$PROJECT_DIR/icon.iconset/icon_512x512@2x.png" +iconutil -c icns "$PROJECT_DIR/icon.iconset" -o "$PROJECT_DIR/icon.icns" + +rm -rf "$PROJECT_DIR/icon.iconset" "$PROJECT_DIR/icon_1024.png" + +echo "icon.icns created: $PROJECT_DIR/icon.icns" diff --git a/packaging/merge_universal2.sh b/packaging/merge_universal2.sh new file mode 100644 index 0000000..a0092eb --- /dev/null +++ b/packaging/merge_universal2.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# Merge arm64 and x86_64 .app bundles into a universal2 .app and create DMG +set -e + +ARM_APP="$1" +INTEL_APP="$2" +OUT_APP="$3" + +if [ -z "$ARM_APP" ] || [ -z "$INTEL_APP" ] || [ -z "$OUT_APP" ]; then + echo "Usage: $0 " + exit 1 +fi + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +DIST_DIR="$PROJECT_DIR/dist" +APP_NAME="$(basename "$OUT_APP" .app)" +DMG_NAME="TgWsProxy" + +# --- Merge --- + +echo "Merging '$ARM_APP' + '$INTEL_APP' -> '$OUT_APP'" + +rm -rf "$OUT_APP" +cp -R "$ARM_APP" "$OUT_APP" + +find "$OUT_APP" -type f | while read -r file; do + rel="${file#"$OUT_APP"/}" + intel_file="$INTEL_APP/$rel" + + [ -f "$intel_file" ] || continue + + if file "$file" | grep -qE "Mach-O (64-bit )?executable|Mach-O (64-bit )?dynamically linked|Mach-O (64-bit )?bundle"; then + arm_arch=$(lipo -archs "$file" 2>/dev/null || echo "") + intel_arch=$(lipo -archs "$intel_file" 2>/dev/null || echo "") + if [ "$arm_arch" = "$intel_arch" ]; then + # same arch (e.g. local test with two arm64 copies) — skip + continue + fi + lipo -create "$file" "$intel_file" -output "$file" + fi +done + +echo "Merge done: $OUT_APP" + +# --- Create DMG --- + +DMG_TEMP="$DIST_DIR/dmg_temp" +rm -rf "$DMG_TEMP" +mkdir -p "$DMG_TEMP" + +cp -R "$OUT_APP" "$DMG_TEMP/" +ln -s /Applications "$DMG_TEMP/Applications" + +hdiutil create \ + -volname "$APP_NAME" \ + -srcfolder "$DMG_TEMP" \ + -ov \ + -format UDZO \ + "$DIST_DIR/$DMG_NAME.dmg" + +rm -rf "$DMG_TEMP" + +echo "DMG created: $DIST_DIR/$DMG_NAME.dmg"