#!/usr/bin/env bash # # Build a Developer-ID-signed DMG from an already-built channel .app. # # scripts/make-dmg.sh {dev|canary|beta|rc|stable} # # Expects dist/.app to exist (run scripts/package-app.sh first). # Produces a compressed DMG with the standard drag-to-/Applications layout, signed # with NUCLEIC_SIGN_ID (the DMG itself is signed so Gatekeeper trusts the container; # notarization of the DMG is a separate step — see scripts/notarize.sh). The DMG file # name is hyphenated (no spaces) so it works cleanly as a GitHub Release asset and a # Sparkle appcast URL; the mounted volume name keeps the human-friendly spaced name. set -euo pipefail CHANNEL="${1:?usage: make-dmg.sh (dev|canary|beta|rc|stable)}" SIGN_ID="${NUCLEIC_SIGN_ID:--}" case "$CHANNEL" in dev) APP_NAME="Nucleic Dev"; SLUG="Nucleic-Dev" ;; canary) APP_NAME="Nucleic Canary"; SLUG="Nucleic-Canary" ;; beta) APP_NAME="Nucleic Beta"; SLUG="Nucleic-Beta" ;; rc) APP_NAME="Nucleic RC"; SLUG="Nucleic-RC" ;; stable) APP_NAME="Nucleic"; SLUG="Nucleic" ;; *) echo "usage: $0 {dev|canary|beta|rc|stable}" >&2; exit 2 ;; esac ROOT="$(cd "$(dirname "$0")/.." && pwd)" APP="$ROOT/dist/$APP_NAME.app" [ -d "$APP" ] || { echo "make-dmg: $APP not found — run scripts/package-app.sh $CHANNEL first" >&2; exit 1; } # Version comes from the bundle so the DMG name matches what package-app.sh stamped. plist="$APP/Contents/Info.plist" SHORT_VERSION="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$plist")" BUILD_NUMBER="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$plist")" DMG="$ROOT/dist/${SLUG}-${SHORT_VERSION}.${BUILD_NUMBER}.dmg" STAGE="$(mktemp -d)" trap 'rm -rf "$STAGE"' EXIT echo "▸ Staging $APP_NAME.app for DMG" cp -R "$APP" "$STAGE/" ln -s /Applications "$STAGE/Applications" echo "▸ Building DMG → $DMG" rm -f "$DMG" hdiutil create \ -volname "$APP_NAME" \ -srcfolder "$STAGE" \ -fs HFS+ \ -format UDZO \ -ov \ "$DMG" >/dev/null # Sign the DMG container itself (a real identity gets a secure timestamp; ad-hoc can't). if [ "$SIGN_ID" = "-" ]; then codesign --force --timestamp=none --sign - "$DMG" echo "✓ $DMG (ad-hoc — local only)" else codesign --force --timestamp --sign "$SIGN_ID" "$DMG" codesign --verify --verbose=1 "$DMG" echo "✓ $DMG (signed: $SIGN_ID)" fi echo " notarize next: scripts/notarize.sh \"$DMG\""