mirror of
https://github.com/Flowseal/tg-ws-proxy.git
synced 2026-06-24 23:41:07 +03:00
ci: add better view on macos installation (#1019)
This commit is contained in:
+30
-36
@@ -272,30 +272,10 @@ jobs:
|
|||||||
python3.12 -m pip install .
|
python3.12 -m pip install .
|
||||||
python3.12 -m pip install pyinstaller==6.13.0
|
python3.12 -m pip install pyinstaller==6.13.0
|
||||||
|
|
||||||
- name: Create macOS icon from ICO
|
- name: Create macOS icon
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
python3.12 - <<'PY'
|
python3.12 macos.py --render-app-icon icon.icns
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
image = Image.open('icon.ico')
|
|
||||||
image = image.resize((1024, 1024), Image.LANCZOS)
|
|
||||||
image.save('icon_1024.png', 'PNG')
|
|
||||||
PY
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
- name: Build app with PyInstaller
|
- name: Build app with PyInstaller
|
||||||
run: python3.12 -m PyInstaller packaging/macos.spec --noconfirm
|
run: python3.12 -m PyInstaller packaging/macos.spec --noconfirm
|
||||||
@@ -303,6 +283,11 @@ jobs:
|
|||||||
- name: Validate universal2 app bundle
|
- name: Validate universal2 app bundle
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
ICON_FILE="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIconFile' \
|
||||||
|
'dist/TG WS Proxy.app/Contents/Info.plist')"
|
||||||
|
test -n "$ICON_FILE"
|
||||||
|
test -f "dist/TG WS Proxy.app/Contents/Resources/$ICON_FILE"
|
||||||
|
|
||||||
found=0
|
found=0
|
||||||
while IFS= read -r -d '' file; do
|
while IFS= read -r -d '' file; do
|
||||||
if file "$file" | grep -q "Mach-O"; then
|
if file "$file" | grep -q "Mach-O"; then
|
||||||
@@ -326,22 +311,31 @@ jobs:
|
|||||||
- name: Create DMG
|
- name: Create DMG
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
APP_NAME="TG WS Proxy"
|
packaging/dmg/build_dmg.sh \
|
||||||
DMG_TEMP="dist/dmg_temp"
|
"dist/TG WS Proxy.app" \
|
||||||
|
"TG WS Proxy" \
|
||||||
rm -rf "$DMG_TEMP"
|
|
||||||
mkdir -p "$DMG_TEMP"
|
|
||||||
cp -R "dist/${APP_NAME}.app" "$DMG_TEMP/"
|
|
||||||
ln -s /Applications "$DMG_TEMP/Applications"
|
|
||||||
|
|
||||||
hdiutil create \
|
|
||||||
-volname "$APP_NAME" \
|
|
||||||
-srcfolder "$DMG_TEMP" \
|
|
||||||
-ov \
|
|
||||||
-format UDZO \
|
|
||||||
"dist/TgWsProxy_macos_universal.dmg"
|
"dist/TgWsProxy_macos_universal.dmg"
|
||||||
|
|
||||||
rm -rf "$DMG_TEMP"
|
- name: Validate DMG
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
for DMG in "dist/TgWsProxy_macos_universal.dmg"; do
|
||||||
|
MOUNT_DIR="$(mktemp -d)"
|
||||||
|
DEVICE="$(hdiutil attach \
|
||||||
|
-readonly \
|
||||||
|
-nobrowse \
|
||||||
|
-mountpoint "$MOUNT_DIR" \
|
||||||
|
"$DMG" \
|
||||||
|
| awk '/^\/dev\// { print $1; exit }')"
|
||||||
|
|
||||||
|
test -d "$MOUNT_DIR/TG WS Proxy.app"
|
||||||
|
test -L "$MOUNT_DIR/Applications"
|
||||||
|
test "$(readlink "$MOUNT_DIR/Applications")" = "/Applications"
|
||||||
|
test -f "$MOUNT_DIR/.background/background.tiff"
|
||||||
|
test -f "$MOUNT_DIR/.DS_Store"
|
||||||
|
hdiutil detach "$DEVICE"
|
||||||
|
rmdir "$MOUNT_DIR"
|
||||||
|
done
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
|
|||||||
@@ -9,16 +9,53 @@ import webbrowser
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
try:
|
|
||||||
import rumps
|
|
||||||
except ImportError:
|
|
||||||
rumps = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
except ImportError:
|
except ImportError:
|
||||||
Image = ImageDraw = ImageFont = None
|
Image = ImageDraw = ImageFont = None
|
||||||
|
|
||||||
|
|
||||||
|
def render_app_icon(size: int):
|
||||||
|
scale = size / 1024
|
||||||
|
image = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
outer = tuple(round(value * scale) for value in (92, 92, 932, 932))
|
||||||
|
draw.ellipse(outer, fill=(0, 151, 221, 255))
|
||||||
|
try:
|
||||||
|
font = ImageFont.truetype(
|
||||||
|
"/System/Library/Fonts/Helvetica.ttc",
|
||||||
|
round(430 * scale),
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
font = ImageFont.load_default()
|
||||||
|
box = draw.textbbox((0, 0), "T", font=font)
|
||||||
|
width = box[2] - box[0]
|
||||||
|
height = box[3] - box[1]
|
||||||
|
draw.text(
|
||||||
|
(
|
||||||
|
(size - width) / 2 - box[0],
|
||||||
|
(size - height) / 2 - box[1] - round(10 * scale),
|
||||||
|
),
|
||||||
|
"T",
|
||||||
|
font=font,
|
||||||
|
fill=(255, 255, 255, 255),
|
||||||
|
)
|
||||||
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__" and len(sys.argv) > 1 and sys.argv[1] == "--render-app-icon":
|
||||||
|
if Image is None:
|
||||||
|
raise SystemExit("Pillow is required to render the macOS app icon")
|
||||||
|
output_path = sys.argv[2] if len(sys.argv) > 2 else "icon.icns"
|
||||||
|
render_app_icon(1024).save(output_path, format="ICNS")
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import rumps
|
||||||
|
except ImportError:
|
||||||
|
rumps = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pyperclip
|
import pyperclip
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -144,26 +181,10 @@ def _ask_cfworker_domain(default: str) -> Optional[str]:
|
|||||||
def _make_menubar_icon(size: int = 44):
|
def _make_menubar_icon(size: int = 44):
|
||||||
if Image is None:
|
if Image is None:
|
||||||
return None
|
return None
|
||||||
img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
return render_app_icon(size)
|
||||||
draw = ImageDraw.Draw(img)
|
|
||||||
margin = size // 11
|
|
||||||
draw.ellipse([margin, margin, size - margin, size - margin], fill=(0, 0, 0, 255))
|
|
||||||
try:
|
|
||||||
font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", size=int(size * 0.55))
|
|
||||||
except Exception:
|
|
||||||
font = ImageFont.load_default()
|
|
||||||
bbox = draw.textbbox((0, 0), "T", font=font)
|
|
||||||
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
|
||||||
draw.text(
|
|
||||||
((size - tw) // 2 - bbox[0], (size - th) // 2 - bbox[1]),
|
|
||||||
"T", fill=(255, 255, 255, 255), font=font,
|
|
||||||
)
|
|
||||||
return img
|
|
||||||
|
|
||||||
|
|
||||||
def _ensure_menubar_icon() -> None:
|
def _ensure_menubar_icon() -> None:
|
||||||
if MENUBAR_ICON_PATH.exists():
|
|
||||||
return
|
|
||||||
ensure_dirs()
|
ensure_dirs()
|
||||||
img = _make_menubar_icon(44)
|
img = _make_menubar_icon(44)
|
||||||
if img:
|
if img:
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Executable
+93
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
APP_PATH="${1:?Usage: build_dmg.sh <App.app> <Volume Name> <output.dmg> [assets_dir]}"
|
||||||
|
VOL_NAME="${2:?missing volume name}"
|
||||||
|
OUT_DMG="${3:?missing output dmg path}"
|
||||||
|
ASSETS_DIR="${4:-$(cd "$(dirname "${BASH_SOURCE[0]}")/assets" && pwd)}"
|
||||||
|
|
||||||
|
WIN_W=660
|
||||||
|
WIN_H=440
|
||||||
|
ICON_SIZE=128
|
||||||
|
APP_X=145
|
||||||
|
APPS_X=515
|
||||||
|
ICON_Y=220
|
||||||
|
|
||||||
|
APP_NAME="$(basename "$APP_PATH")"
|
||||||
|
WORK="$(mktemp -d)"
|
||||||
|
STAGE="$WORK/stage"
|
||||||
|
RW_DMG="$WORK/rw.dmg"
|
||||||
|
MOUNT="/Volumes/$VOL_NAME"
|
||||||
|
DEVICE=""
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [ -n "$DEVICE" ]; then
|
||||||
|
hdiutil detach "$DEVICE" -force >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
rm -rf "$WORK"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
mkdir -p "$STAGE/.background"
|
||||||
|
cp -R "$APP_PATH" "$STAGE/"
|
||||||
|
ln -s /Applications "$STAGE/Applications"
|
||||||
|
|
||||||
|
tiffutil -cathidpicheck \
|
||||||
|
"$ASSETS_DIR/background-light.png" \
|
||||||
|
"$ASSETS_DIR/background-light@2x.png" \
|
||||||
|
-out "$STAGE/.background/background.tiff"
|
||||||
|
|
||||||
|
hdiutil create \
|
||||||
|
-volname "$VOL_NAME" \
|
||||||
|
-srcfolder "$STAGE" \
|
||||||
|
-fs HFS+ \
|
||||||
|
-format UDRW \
|
||||||
|
-ov \
|
||||||
|
"$RW_DMG"
|
||||||
|
|
||||||
|
DEVICE="$(hdiutil attach \
|
||||||
|
-readwrite \
|
||||||
|
-noverify \
|
||||||
|
-noautoopen \
|
||||||
|
-mountpoint "$MOUNT" \
|
||||||
|
"$RW_DMG" \
|
||||||
|
| awk '/^\/dev\// { print $1; exit }')"
|
||||||
|
test -n "$DEVICE"
|
||||||
|
test -d "$MOUNT/$APP_NAME"
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
osascript <<APPLESCRIPT
|
||||||
|
tell application "Finder"
|
||||||
|
tell disk "$VOL_NAME"
|
||||||
|
open
|
||||||
|
set current view of container window to icon view
|
||||||
|
set toolbar visible of container window to false
|
||||||
|
set statusbar visible of container window to false
|
||||||
|
set the bounds of container window to {200, 140, 200 + $WIN_W, 140 + $WIN_H}
|
||||||
|
set theViewOptions to the icon view options of container window
|
||||||
|
set arrangement of theViewOptions to not arranged
|
||||||
|
set icon size of theViewOptions to $ICON_SIZE
|
||||||
|
set text size of theViewOptions to 13
|
||||||
|
set background picture of theViewOptions to file ".background:background.tiff"
|
||||||
|
set position of item "$APP_NAME" of container window to {$APP_X, $ICON_Y}
|
||||||
|
set position of item "Applications" of container window to {$APPS_X, $ICON_Y}
|
||||||
|
close
|
||||||
|
open
|
||||||
|
update
|
||||||
|
delay 2
|
||||||
|
end tell
|
||||||
|
end tell
|
||||||
|
APPLESCRIPT
|
||||||
|
|
||||||
|
SetFile -a C "$MOUNT" 2>/dev/null || true
|
||||||
|
sync
|
||||||
|
|
||||||
|
hdiutil detach "$DEVICE" -force >/dev/null 2>&1 \
|
||||||
|
|| { sleep 3; hdiutil detach "$DEVICE" -force; }
|
||||||
|
DEVICE=""
|
||||||
|
|
||||||
|
rm -f "$OUT_DMG"
|
||||||
|
hdiutil convert "$RW_DMG" -format UDZO -imagekey zlib-level=9 -ov -o "$OUT_DMG"
|
||||||
|
|
||||||
|
echo "Created $OUT_DMG"
|
||||||
Reference in New Issue
Block a user