bundle_app.sh
1#!/bin/bash2# Bundle Haven into a signed .app and zip it for distribution.3#4# Driven by koh offer --builds via kepr.build, which sets KEPR_PLATFORM5# per-platform. macos-aarch64 builds natively; macos-x86_64 cross-compiles6# from Apple Silicon via Swift's --triple flag.7#8# Output: Haven.app.zip in the repo root, ready for koh to upload as the9# release artifact for this platform.10set -euo pipefail1112APP_NAME="Haven"13BUNDLE="${APP_NAME}.app"14ZIP="${APP_NAME}.app.zip"15ENTITLEMENTS="${APP_NAME}.entitlements"1617# Wipe stale outputs so re-runs are idempotent and we never ship a18# half-codesigned bundle left over from a previous attempt. Use the19# project-required `trash` command rather than `rm -rf`: it preserves20# recovery via the macOS Trash, which is the project's standing deletion21# convention. Missing either artifact is fine — `trash` is a no-op for22# paths that don't exist.23trash "${BUNDLE}" "${ZIP}" 2>/dev/null || true2425# Build the executable in release mode. Package.swift declares the product26# as `Haven` (capital H); .build/release is a symlink to27# <arch>-apple-macosx/release so this path works for both native and28# cross-compiled builds.29case "${KEPR_PLATFORM:-}" in30 macos-x86_64)31 echo " → swift build -c release --triple x86_64-apple-macosx"32 swift build -c release --triple x86_64-apple-macosx33 ;;34 macos-aarch64|"")35 echo " → swift build -c release"36 swift build -c release37 ;;38 *)39 echo " → swift build -c release (unknown KEPR_PLATFORM='${KEPR_PLATFORM}', defaulting to native)"40 swift build -c release41 ;;42esac4344# Verify the binary actually materialised where we expect it. Catching this45# here beats waiting for koh's "output binary not found" message at the46# upload step.47if [ ! -f ".build/release/${APP_NAME}" ]; then48 echo "✗ .build/release/${APP_NAME} not produced" >&249 exit 150fi5152# Assemble the bundle. The directory shape is what Finder + codesign expect:53# Contents/MacOS/<executable>, Contents/Info.plist, Contents/Resources/.54mkdir -p "${BUNDLE}/Contents/MacOS"55mkdir -p "${BUNDLE}/Contents/Resources"56cp ".build/release/${APP_NAME}" "${BUNDLE}/Contents/MacOS/"57cp Info.plist "${BUNDLE}/Contents/"58if [ -f AppIcon.icns ]; then59 cp AppIcon.icns "${BUNDLE}/Contents/Resources/"60fi6162# Ad-hoc sign with the project's entitlements. Some macOS APIs require even63# an ad-hoc signature to recognise the bundle as runnable.64codesign --force --deep --entitlements "${ENTITLEMENTS}" --sign - "${BUNDLE}"6566# Zip with ditto — the canonical Apple tool. It preserves resource forks67# and extended attributes, which plain `zip` would mangle, and --keepParent68# stores Haven.app/ at the top of the archive so unzip reproduces the69# standard layout.70ditto -c -k --sequesterRsrc --keepParent "${BUNDLE}" "${ZIP}"7172echo "${ZIP} is ready."