ProdDash Viewer 1.0.0: a Mac app that only shows ProdDash, with a tab per installation
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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c043834d1e
commit
8d8c0ee0c3
Executable
+149
@@ -0,0 +1,149 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user