150 lines
5.3 KiB
Bash
150 lines
5.3 KiB
Bash
#!/bin/zsh
|
|||
|
|
# Build ProdDash Viewer.app — a window onto ProdDash installations, for Apple
|
||
|
|
# silicon Macs. Native Swift, no dependencies.
|
||
|
|
#
|
||
|
|
# ./build.sh build/ProdDash Viewer.app
|
||
|
|
# ./build.sh --dmg …and build/ProdDash-Viewer-<version>-arm64.dmg
|
||
|
|
# ./build.sh --install …and copy it to /Applications
|
||
|
|
# ./build.sh --run …and launch it
|
||
|
|
# ./build.sh --notarize with --dmg: notarise and staple (needs a
|
||
|
|
# Developer ID and a notarytool keychain profile)
|
||
|
|
#
|
||
|
|
# Signing works as ProdDash's own app does (launcher/macos/README.md): a
|
||
|
|
# "Developer ID Application" certificate when one is installed (override with
|
||
|
|
# CODESIGN_IDENTITY), ad-hoc when none is — fine on your own Macs, but another
|
||
|
|
# Mac asks before it opens an ad-hoc app.
|
||
|
|
#
|
||
|
|
# Needs Xcode (xcode-select pointing at it): with the command line tools
|
||
|
|
# alone, the macOS 27 SDK can't expand SwiftUI's @State macro.
|
||
|
|
set -e -u
|
||
|
|
SCRIPT="${0:A}"
|
||
|
|
cd "${SCRIPT:h}"
|
||
|
|
|
||
|
|
APP_NAME="ProdDash Viewer"
|
||
|
|
EXECUTABLE="ProdDashViewer"
|
||
|
|
BUNDLE_ID="org.waterschurch.proddash.viewer"
|
||
|
|
VERSION="1.0.0"
|
||
|
|
ARCH="arm64" # Apple silicon
|
||
|
|
DEPLOYMENT_TARGET="13.0"
|
||
|
|
|
||
|
|
OUT_DIR="build"
|
||
|
|
APP="$OUT_DIR/$APP_NAME.app"
|
||
|
|
CONTENTS="$APP/Contents"
|
||
|
|
|
||
|
|
INSTALL=0
|
||
|
|
RUN=0
|
||
|
|
MAKE_DMG=0
|
||
|
|
NOTARIZE=0
|
||
|
|
|
||
|
|
while [[ $# -gt 0 ]]; do
|
||
|
|
case "$1" in
|
||
|
|
--install) INSTALL=1 ;;
|
||
|
|
--run) RUN=1 ;;
|
||
|
|
--dmg) MAKE_DMG=1 ;;
|
||
|
|
--notarize) NOTARIZE=1; MAKE_DMG=1 ;;
|
||
|
|
-h|--help) sed -n '2,18p' "$SCRIPT"; exit 0 ;;
|
||
|
|
*) echo "build.sh: unknown option $1" >&2; exit 2 ;;
|
||
|
|
esac
|
||
|
|
shift
|
||
|
|
done
|
||
|
|
|
||
|
|
command -v swiftc >/dev/null || {
|
||
|
|
echo "build.sh: swiftc not found — install Xcode, then: sudo xcode-select -s /Applications/Xcode.app" >&2
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
echo "$APP_NAME $VERSION — building for $ARCH"
|
||
|
|
|
||
|
|
rm -rf "$OUT_DIR"
|
||
|
|
mkdir -p "$CONTENTS/MacOS" "$CONTENTS/Resources"
|
||
|
|
|
||
|
|
echo " compiling"
|
||
|
|
swiftc -O -whole-module-optimization \
|
||
|
|
-target "$ARCH-apple-macos$DEPLOYMENT_TARGET" \
|
||
|
|
-module-name ProdDashViewer \
|
||
|
|
-o "$CONTENTS/MacOS/$EXECUTABLE" \
|
||
|
|
Sources/*.swift
|
||
|
|
|
||
|
|
cp Resources/Info.plist "$CONTENTS/Info.plist"
|
||
|
|
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" "$CONTENTS/Info.plist" >/dev/null
|
||
|
|
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $VERSION" "$CONTENTS/Info.plist" >/dev/null
|
||
|
|
printf 'APPL????' > "$CONTENTS/PkgInfo"
|
||
|
|
|
||
|
|
cp -R Resources/Help "$CONTENTS/Resources/Help"
|
||
|
|
|
||
|
|
# Resources/AppIcon.png is drawn by Resources/make-icon.swift — ProdDash's
|
||
|
|
# tile mark on a display, so the Viewer and ProdDash itself tell apart.
|
||
|
|
echo " building the icon"
|
||
|
|
ICONSET="$OUT_DIR/AppIcon.iconset"
|
||
|
|
mkdir -p "$ICONSET"
|
||
|
|
for size in 16 32 128 256 512; do
|
||
|
|
sips -z $size $size Resources/AppIcon.png --out "$ICONSET/icon_${size}x${size}.png" >/dev/null
|
||
|
|
double=$((size * 2))
|
||
|
|
sips -z $double $double Resources/AppIcon.png --out "$ICONSET/icon_${size}x${size}@2x.png" >/dev/null
|
||
|
|
done
|
||
|
|
iconutil -c icns "$ICONSET" -o "$CONTENTS/Resources/AppIcon.icns"
|
||
|
|
rm -rf "$ICONSET"
|
||
|
|
find "$APP" -name '.DS_Store' -delete
|
||
|
|
|
||
|
|
# ── signing ──────────────────────────────────────────────────────────────
|
||
|
|
IDENTITY="${CODESIGN_IDENTITY:-}"
|
||
|
|
if [[ -z "$IDENTITY" ]]; then
|
||
|
|
IDENTITY=$(security find-identity -v -p codesigning 2>/dev/null \
|
||
|
|
| awk -F'"' '/Developer ID Application/ {print $2; exit}')
|
||
|
|
fi
|
||
|
|
if [[ -n "$IDENTITY" ]]; then
|
||
|
|
echo " signing as $IDENTITY"
|
||
|
|
SIGN=(--force --sign "$IDENTITY" --options runtime --timestamp)
|
||
|
|
else
|
||
|
|
echo " signing ad-hoc (no Developer ID installed — other Macs will ask before opening it)"
|
||
|
|
IDENTITY="-"
|
||
|
|
SIGN=(--force --sign -)
|
||
|
|
fi
|
||
|
|
codesign "${SIGN[@]}" --identifier "$BUNDLE_ID" "$APP"
|
||
|
|
codesign --verify --deep --strict "$APP" # stops the build if it doesn't
|
||
|
|
echo " signature verifies"
|
||
|
|
|
||
|
|
echo "built $APP ($(du -sh "$APP" | awk '{print $1}'))"
|
||
|
|
|
||
|
|
# ── disk image ───────────────────────────────────────────────────────────
|
||
|
|
if [[ $MAKE_DMG -eq 1 ]]; then
|
||
|
|
DMG="$OUT_DIR/ProdDash-Viewer-$VERSION-$ARCH.dmg"
|
||
|
|
STAGE="$OUT_DIR/dmg"
|
||
|
|
rm -rf "$STAGE"
|
||
|
|
mkdir -p "$STAGE"
|
||
|
|
cp -R "$APP" "$STAGE/"
|
||
|
|
ln -s /Applications "$STAGE/Applications" # drag-to-install, the usual way
|
||
|
|
hdiutil create -volname "$APP_NAME $VERSION" -srcfolder "$STAGE" \
|
||
|
|
-ov -format UDZO -quiet "$DMG"
|
||
|
|
rm -rf "$STAGE"
|
||
|
|
[[ "$IDENTITY" == "-" ]] || codesign --force --sign "$IDENTITY" --timestamp "$DMG"
|
||
|
|
echo "built $DMG ($(du -sh "$DMG" | awk '{print $1}'))"
|
||
|
|
|
||
|
|
if [[ $NOTARIZE -eq 1 ]]; then
|
||
|
|
if [[ "$IDENTITY" == "-" ]]; then
|
||
|
|
echo "build.sh: can't notarise an ad-hoc signed app — a Developer ID is required" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
PROFILE="${NOTARY_PROFILE:-proddash}"
|
||
|
|
echo " notarising as keychain profile \"$PROFILE\" (a few minutes)"
|
||
|
|
xcrun notarytool submit "$DMG" --keychain-profile "$PROFILE" --wait
|
||
|
|
xcrun stapler staple "$DMG"
|
||
|
|
echo " notarised and stapled — this opens anywhere with no warning"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [[ $INSTALL -eq 1 ]]; then
|
||
|
|
DEST="/Applications/$APP_NAME.app"
|
||
|
|
if pgrep -x "$EXECUTABLE" >/dev/null; then
|
||
|
|
osascript -e "tell application id \"$BUNDLE_ID\" to quit" >/dev/null 2>&1 || true
|
||
|
|
sleep 1
|
||
|
|
fi
|
||
|
|
rm -rf "$DEST"
|
||
|
|
cp -R "$APP" "$DEST"
|
||
|
|
echo "installed $DEST"
|
||
|
|
APP="$DEST"
|
||
|
|
fi
|
||
|
|
|
||
|
|
[[ $RUN -eq 1 ]] && open "$APP"
|
||
|
|
exit 0
|