A new Apple-silicon app in viewer/macos (Swift, no dependencies, ~1 MB): one window showing a ProdDash dashboard in WebKit, and nothing else. It runs no server; it points at any ProdDash on the network. - Connections (name + address) in Settings; the add/edit sheet asks /api/modules whether ProdDash answers as you type. No port means 24500. - Two or more connections bring a slim tab bar of their names. In a window it floats over the dashboard and comes out while the pointer is on the title bar; it is not part of the title bar, because anything there makes WebKit push the page down. In full screen it is a titlebar accessory that comes down with the menu bar, and it flashes up after each switch. ⌃Tab / ⌃⇧Tab and ⌘1–9 switch from the keyboard. - Full screen, reopening in full screen if it quit there; window size and the selected tab are kept too. The dashboard's own full-screen button works as well. - Menus: View (Back, Forward, Reload Page, Enter Full Screen), Settings, Edit (copy/paste in the page's text fields), Window, Help (a guide that ships in the app). - Every installation keeps its own live web view. One that can't be reached says why and retries at 2, 4, 8, then every 15 s; a redirect off the installation is reported, not handed to the browser. JS alert, confirm and prompt show as sheets; links to other sites open in the browser. - General settings: open at login (SMAppService), keep the display awake while a dashboard is on screen (default on). build.sh signs with the Developer ID when one is installed and makes a notarised DMG with --notarize, as the launcher's does. It needs Xcode: with only the command line tools the macOS 27 SDK can't expand SwiftUI's @State. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
150 lines
5.3 KiB
Bash
Executable File
150 lines
5.3 KiB
Bash
Executable File
#!/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
|