| 1 | #!/usr/bin/env bash
|
|---|
| 2 | #
|
|---|
| 3 | # mac_package.sh - turn a freshly built bnc.app into something that runs on
|
|---|
| 4 | # another Mac without requiring a paid Apple Developer ID.
|
|---|
| 5 | #
|
|---|
| 6 | # Purpose: qmake/make on macOS produces src/../bnc.app (a plain Qt app
|
|---|
| 7 | # bundle, unsigned, with no bundled Qt frameworks). This script
|
|---|
| 8 | # - bundles the required Qt frameworks/plugins via macdeployqt
|
|---|
| 9 | # - ad-hoc code-signs the whole bundle (no Apple account needed;
|
|---|
| 10 | # required on Apple Silicon just to let the binary launch)
|
|---|
| 11 | # - optionally signs with a real "Developer ID Application"
|
|---|
| 12 | # identity if CODESIGN_IDENTITY is set in the environment
|
|---|
| 13 | # - packages the result as a dmg (drag-to-Applications, the
|
|---|
| 14 | # expected format for external users) and/or a zip
|
|---|
| 15 | #
|
|---|
| 16 | # Usage: scripts/mac_package.sh [--format dmg|zip|both] [path/to/bnc.app]
|
|---|
| 17 | # CODESIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)" \
|
|---|
| 18 | # scripts/mac_package.sh
|
|---|
| 19 | #
|
|---|
| 20 | # Note: Without a real Developer ID + notarization, macOS Gatekeeper will
|
|---|
| 21 | # still show an "unidentified developer" warning the first time a
|
|---|
| 22 | # downloaded copy is opened. Tell recipients to right-click (or
|
|---|
| 23 | # Control-click) the app and choose "Open" once, or run
|
|---|
| 24 | # `xattr -cr bnc.app` after unpacking, to bypass it. No signature
|
|---|
| 25 | # purchase is required for local use or for users willing to do
|
|---|
| 26 | # that one-time step.
|
|---|
| 27 |
|
|---|
| 28 | set -euo pipefail
|
|---|
| 29 |
|
|---|
| 30 | if [[ "$(uname -s)" != "Darwin" ]]; then
|
|---|
| 31 | echo "error: this script must be run on macOS" >&2
|
|---|
| 32 | exit 1
|
|---|
| 33 | fi
|
|---|
| 34 |
|
|---|
| 35 | FORMAT="dmg"
|
|---|
| 36 | APP_PATH=""
|
|---|
| 37 | while [[ $# -gt 0 ]]; do
|
|---|
| 38 | case "$1" in
|
|---|
| 39 | --format)
|
|---|
| 40 | FORMAT="$2"
|
|---|
| 41 | shift 2
|
|---|
| 42 | ;;
|
|---|
| 43 | *)
|
|---|
| 44 | APP_PATH="$1"
|
|---|
| 45 | shift
|
|---|
| 46 | ;;
|
|---|
| 47 | esac
|
|---|
| 48 | done
|
|---|
| 49 | case "$FORMAT" in
|
|---|
| 50 | dmg|zip|both) ;;
|
|---|
| 51 | *) echo "error: --format must be dmg, zip, or both (got '$FORMAT')" >&2; exit 1 ;;
|
|---|
| 52 | esac
|
|---|
| 53 |
|
|---|
| 54 | APP_PATH="${APP_PATH:-bnc.app}"
|
|---|
| 55 | if [[ ! -d "$APP_PATH" ]]; then
|
|---|
| 56 | # fall back to the usual qmake output location (src.pro: TARGET = ../bnc)
|
|---|
| 57 | if [[ -d "$(dirname "$0")/../bnc.app" ]]; then
|
|---|
| 58 | APP_PATH="$(dirname "$0")/../bnc.app"
|
|---|
| 59 | else
|
|---|
| 60 | echo "error: could not find bnc.app (looked for '$APP_PATH')" >&2
|
|---|
| 61 | echo " build it first (qmake && make), then pass its path explicitly" >&2
|
|---|
| 62 | exit 1
|
|---|
| 63 | fi
|
|---|
| 64 | fi
|
|---|
| 65 | APP_PATH="$(cd "$(dirname "$APP_PATH")" && pwd)/$(basename "$APP_PATH")"
|
|---|
| 66 | APP_NAME="$(basename "$APP_PATH" .app)"
|
|---|
| 67 |
|
|---|
| 68 | command -v macdeployqt >/dev/null 2>&1 || {
|
|---|
| 69 | echo "error: macdeployqt not found on PATH (it ships with Qt, e.g. <QtDir>/bin)" >&2
|
|---|
| 70 | exit 1
|
|---|
| 71 | }
|
|---|
| 72 |
|
|---|
| 73 | echo "== bundling Qt frameworks/plugins into $APP_PATH =="
|
|---|
| 74 | macdeployqt "$APP_PATH"
|
|---|
| 75 |
|
|---|
| 76 | if [[ -n "${CODESIGN_IDENTITY:-}" ]]; then
|
|---|
| 77 | echo "== signing with identity: $CODESIGN_IDENTITY =="
|
|---|
| 78 | codesign --force --deep --options runtime --sign "$CODESIGN_IDENTITY" "$APP_PATH"
|
|---|
| 79 | echo " (remember to notarize + staple for Gatekeeper-clean distribution:"
|
|---|
| 80 | echo " xcrun notarytool submit <dmg-or-zip> --keychain-profile <profile> --wait"
|
|---|
| 81 | echo " xcrun stapler staple \"$APP_PATH\")"
|
|---|
| 82 | else
|
|---|
| 83 | echo "== ad-hoc signing (no Apple account required) =="
|
|---|
| 84 | codesign --force --deep --sign - "$APP_PATH"
|
|---|
| 85 | fi
|
|---|
| 86 |
|
|---|
| 87 | echo "== verifying signature =="
|
|---|
| 88 | codesign --verify --deep --strict --verbose=2 "$APP_PATH"
|
|---|
| 89 |
|
|---|
| 90 | if [[ "$FORMAT" == "zip" || "$FORMAT" == "both" ]]; then
|
|---|
| 91 | ZIP_PATH="${APP_PATH%.app}.zip"
|
|---|
| 92 | echo "== packaging $ZIP_PATH =="
|
|---|
| 93 | rm -f "$ZIP_PATH"
|
|---|
| 94 | # ditto preserves the code signature; plain zip/tar can corrupt it
|
|---|
| 95 | ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
|
|---|
| 96 | echo "Done: $ZIP_PATH"
|
|---|
| 97 | fi
|
|---|
| 98 |
|
|---|
| 99 | if [[ "$FORMAT" == "dmg" || "$FORMAT" == "both" ]]; then
|
|---|
| 100 | DMG_PATH="${APP_PATH%.app}.dmg"
|
|---|
| 101 | echo "== packaging $DMG_PATH =="
|
|---|
| 102 | rm -f "$DMG_PATH"
|
|---|
| 103 | STAGING_DIR="$(mktemp -d)"
|
|---|
| 104 | trap 'rm -rf "$STAGING_DIR"' EXIT
|
|---|
| 105 | ditto "$APP_PATH" "$STAGING_DIR/$APP_NAME.app"
|
|---|
| 106 | ln -s /Applications "$STAGING_DIR/Applications"
|
|---|
| 107 | hdiutil create -volname "$APP_NAME" -srcfolder "$STAGING_DIR" -ov -format UDZO "$DMG_PATH"
|
|---|
| 108 | echo "Done: $DMG_PATH"
|
|---|
| 109 | fi
|
|---|
| 110 |
|
|---|
| 111 | echo
|
|---|
| 112 | echo "Recipients of an unsigned/ad-hoc build must bypass Gatekeeper once:"
|
|---|
| 113 | echo " - right-click the app -> Open -> confirm, or"
|
|---|
| 114 | echo " - run: xattr -cr \"$APP_NAME.app\" after mounting/unzipping"
|
|---|