113 lines
4.6 KiB
Bash
Executable File
113 lines
4.6 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# DBSyn — Developer ID direct distribution (signed and notarized app + DMG)
|
|
# Install at: <project>/scripts/release-dbsyn-direct.sh
|
|
# Run from any directory: ./scripts/release-dbsyn-direct.sh
|
|
|
|
APP_NAME="DBSyn"
|
|
SCHEME="DBSyn"
|
|
PROJECT="DBSyn.xcodeproj"
|
|
TEAM_ID="${TEAM_ID:-39R6U3437D}"
|
|
SIGNING_IDENTITY="${SIGNING_IDENTITY:-B99D353C88996866C49E4389B375645F76870A3F}"
|
|
NOTARY_PROFILE="${NOTARY_PROFILE:-orion-notary}"
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
if [[ -d "$SCRIPT_DIR/$PROJECT" ]]; then
|
|
ROOT_DIR="$SCRIPT_DIR"
|
|
elif [[ -d "$SCRIPT_DIR/../$PROJECT" ]]; then
|
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
else
|
|
echo "ERROR: $PROJECT not found next to this script or in its parent directory." >&2
|
|
exit 1
|
|
fi
|
|
|
|
BUILD_DIR="$ROOT_DIR/build/direct-distribution"
|
|
ARCHIVE_PATH="$BUILD_DIR/$APP_NAME.xcarchive"
|
|
EXPORT_DIR="$BUILD_DIR/export"
|
|
DMG_STAGE="$BUILD_DIR/dmg-stage"
|
|
EXPORT_OPTIONS="$BUILD_DIR/ExportOptions.plist"
|
|
APP_ZIP="$BUILD_DIR/$APP_NAME-notarization.zip"
|
|
RELEASE_DIR="$ROOT_DIR/releases"
|
|
DMG_PATH="$RELEASE_DIR/$APP_NAME.dmg"
|
|
|
|
for tool in xcodebuild xcrun codesign spctl ditto hdiutil shasum security; do
|
|
command -v "$tool" >/dev/null || { echo "ERROR: Missing $tool" >&2; exit 1; }
|
|
done
|
|
[[ -d "$ROOT_DIR/$PROJECT" ]] || { echo "ERROR: Missing $PROJECT" >&2; exit 1; }
|
|
security find-identity -v -p codesigning | grep -Fq "$SIGNING_IDENTITY" || {
|
|
echo "ERROR: Signing identity $SIGNING_IDENTITY not available in Keychain." >&2
|
|
exit 1
|
|
}
|
|
# Check that notarytool profile exists before spending time compiling.
|
|
xcrun notarytool history --keychain-profile "$NOTARY_PROFILE" >/dev/null || {
|
|
echo "ERROR: Notarytool profile '$NOTARY_PROFILE' is unavailable or invalid." >&2
|
|
exit 1
|
|
}
|
|
|
|
printf '\n========== DBSyn DIRECT DISTRIBUTION ==========\n'
|
|
cd "$ROOT_DIR"
|
|
rm -rf "$BUILD_DIR"
|
|
mkdir -p "$BUILD_DIR" "$EXPORT_DIR" "$DMG_STAGE" "$RELEASE_DIR"
|
|
# Keep previous releases until a new notarized DMG has been successfully built.
|
|
|
|
cat > "$EXPORT_OPTIONS" <<PLIST
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0"><dict>
|
|
<key>method</key><string>developer-id</string>
|
|
<key>signingStyle</key><string>automatic</string>
|
|
<key>teamID</key><string>$TEAM_ID</string>
|
|
</dict></plist>
|
|
PLIST
|
|
|
|
echo '==> Archiving DBSyn (Release)...'
|
|
xcodebuild -project "$PROJECT" -scheme "$SCHEME" -configuration Release \
|
|
-destination 'generic/platform=macOS' -archivePath "$ARCHIVE_PATH" \
|
|
DEVELOPMENT_TEAM="$TEAM_ID" CODE_SIGN_STYLE=Automatic \
|
|
clean archive
|
|
|
|
echo '==> Exporting Developer ID application...'
|
|
xcodebuild -exportArchive -archivePath "$ARCHIVE_PATH" \
|
|
-exportPath "$EXPORT_DIR" -exportOptionsPlist "$EXPORT_OPTIONS"
|
|
APP_PATH="$EXPORT_DIR/$APP_NAME.app"
|
|
[[ -d "$APP_PATH" ]] || { echo "ERROR: Exported app missing: $APP_PATH" >&2; exit 1; }
|
|
|
|
echo '==> Checking application signature and hardened runtime...'
|
|
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
|
|
codesign -dv --verbose=4 "$APP_PATH" 2>&1 | grep -E 'Identifier=|Authority=|TeamIdentifier=|Runtime Version=|flags=' || true
|
|
ACTUAL_TEAM="$(codesign -dv --verbose=4 "$APP_PATH" 2>&1 | sed -n 's/^TeamIdentifier=//p' | head -1)"
|
|
[[ "$ACTUAL_TEAM" == "$TEAM_ID" ]] || { echo "ERROR: Unexpected signing team: $ACTUAL_TEAM" >&2; exit 1; }
|
|
codesign -dv --verbose=4 "$APP_PATH" 2>&1 | grep -q 'runtime' || {
|
|
echo 'ERROR: App is not signed with the hardened runtime required for notarization.' >&2
|
|
exit 1
|
|
}
|
|
|
|
echo '==> Notarizing application...'
|
|
ditto -c -k --keepParent "$APP_PATH" "$APP_ZIP"
|
|
xcrun notarytool submit "$APP_ZIP" --keychain-profile "$NOTARY_PROFILE" --wait
|
|
xcrun stapler staple "$APP_PATH"
|
|
xcrun stapler validate "$APP_PATH"
|
|
spctl --assess --type execute --verbose=4 "$APP_PATH"
|
|
|
|
echo '==> Creating installer DMG...'
|
|
ditto "$APP_PATH" "$DMG_STAGE/$APP_NAME.app"
|
|
ln -s /Applications "$DMG_STAGE/Applications"
|
|
TMP_DMG="$BUILD_DIR/$APP_NAME.dmg"
|
|
hdiutil create -volname "$APP_NAME" -srcfolder "$DMG_STAGE" -ov -format UDZO "$TMP_DMG"
|
|
|
|
echo '==> Signing and notarizing DMG...'
|
|
codesign --force --timestamp --sign "$SIGNING_IDENTITY" "$TMP_DMG"
|
|
codesign --verify --verbose=2 "$TMP_DMG"
|
|
xcrun notarytool submit "$TMP_DMG" --keychain-profile "$NOTARY_PROFILE" --wait
|
|
xcrun stapler staple "$TMP_DMG"
|
|
xcrun stapler validate "$TMP_DMG"
|
|
spctl --assess --type open --context context:primary-signature --verbose=4 "$TMP_DMG"
|
|
|
|
# Publish only after all checks have passed.
|
|
cp -f "$TMP_DMG" "$DMG_PATH"
|
|
echo '==> Release ready:'
|
|
ls -lh "$DMG_PATH"
|
|
shasum -a 256 "$DMG_PATH"
|
|
printf '\n========== DBSyn RELEASE COMPLETE ==========\n%s\n' "$DMG_PATH"
|