Added runtime test #22
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
| name: macOS DMG | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: [master] | |
| tags: ["[0-9]*.[0-9]*.[0-9]*"] | |
| pull_request: | |
| paths: | |
| - ".github/workflows/macos-dmg.yml" | |
| - "meson.build" | |
| - "data/**" | |
| - "src/**" | |
| - "macos/**" | |
| jobs: | |
| build-dmg: | |
| strategy: | |
| matrix: | |
| os: [macos-13, macos-14] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Show runner info | |
| run: | | |
| uname -a | |
| sw_vers | |
| sysctl -n machdep.cpu.brand_string || true | |
| - name: Setup Homebrew and dependencies | |
| run: | | |
| brew update-reset | |
| brew install meson ninja pkg-config glib gtk4 libadwaita pygobject3 gtksourceview5 [email protected] | |
| brew --version | |
| brew list --versions | |
| - name: Build gtk-mac-bundler from source | |
| run: | | |
| git clone https://gitlab.gnome.org/GNOME/gtk-mac-bundler.git | |
| cd gtk-mac-bundler | |
| make | |
| sudo make install | |
| which gtk-mac-bundler | |
| gtk-mac-bundler --version || true | |
| - name: Build blueprint-compiler from git (v0.18.0) | |
| run: | | |
| set -euxo pipefail | |
| git clone --depth 1 --branch v0.18.0 https://gitlab.gnome.org/GNOME/blueprint-compiler.git | |
| cd blueprint-compiler | |
| meson setup build --prefix "$PWD/../bp_prefix" --buildtype=release | |
| meson compile -C build | |
| meson install -C build | |
| echo "$GITHUB_WORKSPACE/bp_prefix/bin" >> "$GITHUB_PATH" | |
| - name: Build project (Meson) | |
| run: | | |
| set -euxo pipefail | |
| PY_BIN="$(brew --prefix [email protected])/bin/python3" | |
| echo "Using Python: ${PY_BIN}" | |
| export PYTHON="${PY_BIN}" | |
| meson setup build --prefix "$PWD/stage" --buildtype=release | |
| meson compile -C build | |
| meson install -C build | |
| - name: Bundle app with gtk-mac-bundler | |
| run: | | |
| mkdir -p macos | |
| BREW_PREFIX="$(brew --prefix)" | |
| export BREW_PREFIX | |
| echo "BREW_PREFIX=${BREW_PREFIX}" >> "$GITHUB_ENV" | |
| echo "Detected Homebrew prefix: $BREW_PREFIX" | |
| # Write Info.plist required by gtk-mac-bundler | |
| cat > macos/Info.plist << 'PLIST' | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| <plist version="1.0"> | |
| <dict> | |
| <key>CFBundleIdentifier</key> | |
| <string>io.github.BuddySirJava.SSH-Studio</string> | |
| <key>CFBundleName</key> | |
| <string>SSH Studio</string> | |
| <key>CFBundleExecutable</key> | |
| <string>ssh-studio</string> | |
| <key>CFBundleShortVersionString</key> | |
| <string>1.0.0</string> | |
| <key>CFBundleVersion</key> | |
| <string>1.0.0</string> | |
| <key>CFBundlePackageType</key> | |
| <string>APPL</string> | |
| <key>CFBundleSignature</key> | |
| <string>SSHS</string> | |
| </dict> | |
| </plist> | |
| PLIST | |
| # Bundle description for gtk-mac-bundler | |
| cat > macos/ssh-studio.bundle << 'XML' | |
| <?xml version="1.0"?> | |
| <app-bundle> | |
| <meta> | |
| <prefix>${env:BREW_PREFIX}</prefix> | |
| <destination overwrite="yes">${project}/../build/ssh-studio.bundle</destination> | |
| <gtk>gtk4</gtk> | |
| </meta> | |
| <id>io.github.BuddySirJava.SSH-Studio</id> | |
| <name>SSH Studio</name> | |
| <version>1.0.0</version> | |
| <icon>${project}/../data/media/icon_512.png</icon> | |
| <plist>${env:GITHUB_WORKSPACE}/macos/Info.plist</plist> | |
| <main-binary dest="${bundle}/Contents/MacOS/${name}">${project}/../stage/bin/ssh-studio</main-binary> | |
| <data dest="${bundle}/Contents/Resources/share/io.github.BuddySirJava.SSH-Studio/ssh-studio-resources.gresource">${project}/../stage/share/io.github.BuddySirJava.SSH-Studio/ssh-studio-resources.gresource</data> | |
| <data dest="${bundle}/Contents/Resources/src">${project}/../src</data> | |
| </app-bundle> | |
| XML | |
| - name: Run gtk-mac-bundler | |
| run: | | |
| set -euxo pipefail | |
| BREW_PREFIX="$(brew --prefix)" | |
| export BREW_PREFIX | |
| gtk-mac-bundler macos/ssh-studio.bundle | |
| - name: Build self-contained .app (vendor Python + GTK) | |
| run: | | |
| set -euxo pipefail | |
| APP="dist/SSH Studio.app" | |
| APPROOT="$APP/Contents" | |
| MACOS="$APPROOT/MacOS" | |
| RES="$APPROOT/Resources" | |
| FRAMEWORKS="$APPROOT/Frameworks" | |
| mkdir -p "$MACOS" "$RES/python" "$FRAMEWORKS" | |
| # 1) Copy your Python sources | |
| rsync -a src/ "$RES/python/ssh_studio/" | |
| # 2) Copy compiled GResource from Meson install | |
| APP_ID="io.github.BuddySirJava.SSH-Studio" | |
| install -m 0644 "stage/share/$APP_ID/ssh-studio-resources.gresource" "$RES/" | |
| # 3) Vendor Python.framework | |
| BREW_PREFIX="$(brew --prefix)" | |
| rsync -a "$BREW_PREFIX/Frameworks/Python.framework" "$FRAMEWORKS/" | |
| # 4) Vendor GTK & friends (most-used libs) | |
| for p in glib gtk4 libadwaita gtksourceview5 gdk-pixbuf pango cairo harfbuzz fribidi graphite2 libpng jpeg libtiff libepoxy libffi gettext; do | |
| if [ -d "$BREW_PREFIX/opt/$p/lib" ]; then | |
| mkdir -p "$FRAMEWORKS/$p/lib" | |
| rsync -a "$BREW_PREFIX/opt/$p/lib/" "$FRAMEWORKS/$p/lib/" | |
| fi | |
| if [ -d "$BREW_PREFIX/opt/$p/lib/girepository-1.0" ]; then | |
| mkdir -p "$RES/girepository-1.0" | |
| rsync -a "$BREW_PREFIX/opt/$p/lib/girepository-1.0/" "$RES/girepository-1.0/" | |
| fi | |
| if [ -d "$BREW_PREFIX/opt/$p/share" ]; then | |
| mkdir -p "$RES/share/$p" | |
| rsync -a "$BREW_PREFIX/opt/$p/share/" "$RES/share/" | |
| fi | |
| done | |
| # 4b) Vendor PyGObject (gi) and PyCairo into bundled Python path | |
| for SITE in \ | |
| "$BREW_PREFIX/lib/python3.13/site-packages" \ | |
| "$BREW_PREFIX/opt/pygobject3/lib/python3.13/site-packages" \ | |
| "$BREW_PREFIX/opt/py3cairo/lib/python3.13/site-packages"; do | |
| if [ -d "$SITE/gi" ]; then | |
| rsync -a "$SITE/gi" "$RES/python/" | |
| fi | |
| if [ -d "$SITE/cairo" ]; then | |
| rsync -a "$SITE/cairo" "$RES/python/" | |
| fi | |
| done | |
| # 5) Schemas needed by GSettings | |
| mkdir -p "$RES/share/glib-2.0/schemas" | |
| rsync -a "$BREW_PREFIX/opt/glib/share/glib-2.0/schemas/" "$RES/share/glib-2.0/schemas/" | |
| glib-compile-schemas "$RES/share/glib-2.0/schemas" | |
| # 6) Launcher that sets env so app uses bundled runtimes | |
| cat > "$MACOS/ssh-studio" <<'SH' | |
| #!/bin/bash | |
| SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | |
| RES="$SCRIPT_DIR/../Resources" | |
| FW="$SCRIPT_DIR/../Frameworks" | |
| export PYTHONHOME="$FW/Python.framework/Versions/3.13" | |
| export PYTHONPATH="$RES/python" | |
| export DYLD_FALLBACK_LIBRARY_PATH="$FW:$FW/glib/lib:$FW/gtk4/lib:$FW/libadwaita/lib:$FW/pango/lib:$FW/cairo/lib:$FW/gtksourceview5/lib" | |
| export GI_TYPELIB_PATH="$RES/girepository-1.0" | |
| export XDG_DATA_DIRS="$RES/share" | |
| export GSETTINGS_SCHEMA_DIR="$RES/share/glib-2.0/schemas" | |
| export GTK_DATA_PREFIX="$RES" | |
| # Register GResource then run app | |
| "$PYTHONHOME/bin/python3" - <<'PY' | |
| import os, sys | |
| from gi.repository import Gio | |
| res_path = os.path.join(os.path.dirname(__file__), '..', 'Resources', 'ssh-studio-resources.gresource') | |
| Gio.resources_register(Gio.Resource.load(res_path)) | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'Resources', 'python')) | |
| from ssh_studio import main as _main | |
| sys.exit(_main.main()) | |
| PY | |
| SH | |
| chmod 0755 "$MACOS/ssh-studio" | |
| # 7) Minimal Info.plist (if you’re not generating it already) | |
| cat > "$APPROOT/Info.plist" <<'PLIST' | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| <plist version="1.0"><dict> | |
| <key>CFBundleIdentifier</key><string>io.github.BuddySirJava.SSH-Studio</string> | |
| <key>CFBundleName</key><string>SSH Studio</string> | |
| <key>CFBundleExecutable</key><string>ssh-studio</string> | |
| <key>CFBundlePackageType</key><string>APPL</string> | |
| <key>LSMinimumSystemVersion</key><string>11.0</string> | |
| </dict></plist> | |
| PLIST | |
| - name: List .app contents | |
| run: | | |
| set -euxo pipefail | |
| echo 'gtk-mac-bundler app:' || true | |
| ls -R build/ssh-studio.bundle/SSH\ Studio.app/Contents || true | |
| echo 'self-contained app:' || true | |
| ls -R 'dist/SSH Studio.app/Contents' || true | |
| - name: Smoke test self-contained app | |
| run: | | |
| set -euxo pipefail | |
| LAUNCHER='dist/SSH Studio.app/Contents/MacOS/ssh-studio' | |
| test -x "$LAUNCHER" | |
| # CLI help should exit quickly without requiring a GUI session | |
| "$LAUNCHER" --help || true | |
| # Also try launching briefly, then kill | |
| "$LAUNCHER" & | |
| sleep 2 || true | |
| pkill -f "SSH Studio" || true | |
| pkill -f "ssh-studio" || true | |
| - name: Create DMG | |
| run: | | |
| set -euxo pipefail | |
| VER=$(sed -n "s/.*version: '\([^']*\)'.*/\1/p" meson.build | head -n1) | |
| ARCH=$(uname -m) | |
| mkdir -p dmgroot | |
| # Prefer self-contained app for distribution | |
| cp -R "dist/SSH Studio.app" "dmgroot/SSH Studio.app" | |
| ln -s /Applications dmgroot/Applications | |
| hdiutil create -volname "SSH Studio" -srcfolder dmgroot -ov -fs HFS+ "ssh-studio-${VER}-${ARCH}.dmg" | |
| - name: Mount DMG and smoke test app | |
| run: | | |
| set -euxo pipefail | |
| DMG="$(ls -1 ssh-studio-*.dmg | head -n1)" | |
| MNT="$(mktemp -d /tmp/sshstudio.XXXX)" | |
| hdiutil attach "$DMG" -mountpoint "$MNT" -nobrowse -quiet | |
| ls -la "$MNT" | |
| LAUNCHER="$MNT/SSH Studio.app/Contents/MacOS/ssh-studio" | |
| test -x "$LAUNCHER" | |
| # CLI help should execute; ignore non-zero in case GTK prints warnings | |
| "$LAUNCHER" --help || true | |
| # Briefly launch then terminate | |
| "$LAUNCHER" & | |
| sleep 2 || true | |
| pkill -f "SSH Studio" || true | |
| pkill -f "ssh-studio" || true | |
| hdiutil detach "$MNT" -quiet || true | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ssh-studio-dmg-${{ matrix.os }} | |
| path: | | |
| *.dmg |