diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..9a897b6
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/RsyncStudio.iml b/.idea/RsyncStudio.iml
new file mode 100644
index 0000000..6102194
--- /dev/null
+++ b/.idea/RsyncStudio.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..f31f777
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..4603f60
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index c6741ac..e3b153a 100755
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ Background automation remains active while the application is running, including
This project targets Developer ID signed/notarised direct distribution, not the Mac App Store. Current hardening includes Hardened Runtime, validated rsync executables, protected advanced rsync arguments, non-interactive SSH with timeouts/keepalives, normal SSH host-key verification, private-key/agent authentication only, unattended `--delete` authorisation, local source/destination overlap protection, sanitised/capped run logs, configurable history retention, a privacy manifest, and persistent-store recovery mode.
-See `Docs/RELEASE.md` and run `Scripts/security-check.sh` before producing a release archive.
+See `Docs/RELEASE.md` and run `Scripts/old/security-check.sh` before producing a release archive.
## Professional reliability features
@@ -54,4 +54,4 @@ The production build includes job preflight health checks, destination-volume id
## Commercial direct-distribution build
-On the release Mac, `Scripts/release.sh` creates the Developer ID signed, notarized and stapled DMG plus its SHA-256 checksum. The script never publishes automatically. See `Docs/COMMERCIAL_RELEASE_STAGE2.md`.
+On the release Mac, `Scripts/old/release.sh` creates the Developer ID signed, notarized and stapled DMG plus its SHA-256 checksum. The script never publishes automatically. See `Docs/COMMERCIAL_RELEASE_STAGE2.md`.
diff --git a/Rsync Studio/Resources/Rsync Studio Help.md b/Rsync Studio/Resources/Rsync Studio Help.md
index 91baef3..ac794df 100755
--- a/Rsync Studio/Resources/Rsync Studio Help.md
+++ b/Rsync Studio/Resources/Rsync Studio Help.md
@@ -548,7 +548,7 @@ The bundled privacy manifest declares no tracking and no collected data.
## Release verification
-The project contains `Docs/RELEASE.md` and `Scripts/security-check.sh` with Developer ID, notarisation, signature and Gatekeeper verification steps for production builds.
+The project contains `Docs/RELEASE.md` and `../../Scripts/old/security-check.sh` with Developer ID, notarisation, signature and Gatekeeper verification steps for production builds.
# Production Reliability
diff --git a/Scripts/build.sh b/Scripts/build.sh
new file mode 100644
index 0000000..f4c4756
--- /dev/null
+++ b/Scripts/build.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+cd /Volumes/Developer/Applications/DbSyn
+
+# Remove previous build
+rm -rf ./build
+
+# Clean and build DBSyn
+xcodebuild \
+ -project DBSyn.xcodeproj \
+ -scheme DBSyn \
+ -configuration Debug \
+ -destination 'platform=macOS,arch=arm64' \
+ -derivedDataPath ./build \
+ clean build
+
+# Stop any running instance
+pkill -x DBSyn 2>/dev/null || true
+
+# Launch the newly built application
+open ./build/Build/Products/Debug/DBSyn.app
\ No newline at end of file
diff --git a/Scripts/license-keygen.swift b/Scripts/old/license-keygen.swift
similarity index 100%
rename from Scripts/license-keygen.swift
rename to Scripts/old/license-keygen.swift
diff --git a/Scripts/license-sign.swift b/Scripts/old/license-sign.swift
similarity index 100%
rename from Scripts/license-sign.swift
rename to Scripts/old/license-sign.swift
diff --git a/Scripts/release-preflight.sh b/Scripts/old/release-preflight.sh
similarity index 100%
rename from Scripts/release-preflight.sh
rename to Scripts/old/release-preflight.sh
diff --git a/Scripts/old/release.sh b/Scripts/old/release.sh
new file mode 100755
index 0000000..327c28f
--- /dev/null
+++ b/Scripts/old/release.sh
@@ -0,0 +1,206 @@
+#!/bin/bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+PROJECT="$ROOT/Rsync Studio.xcodeproj"
+SCHEME="Rsync Studio"
+CONFIGURATION="Release"
+PRODUCT_NAME="Rsync Studio"
+DISPLAY_NAME="Rsync Studio"
+BUNDLE_ID="net.absdev.RsyncStudio"
+NOTARY_PROFILE="${NOTARY_PROFILE:-Rsync Studio Notary}"
+RELEASE_ROOT="${RELEASE_ROOT:-$ROOT/build/releases}"
+SKIP_PREFLIGHT=0
+SKIP_NOTARIZATION=0
+
+usage() {
+ cat <&2; exit 2; }; NOTARY_PROFILE="$2"; shift 2 ;;
+ --output) [[ $# -ge 2 ]] || { echo "Missing value for $1" >&2; exit 2; }; RELEASE_ROOT="$2"; shift 2 ;;
+ --skip-preflight) SKIP_PREFLIGHT=1; shift ;;
+ --skip-notarization) SKIP_NOTARIZATION=1; shift ;;
+ -h|--help) usage; exit 0 ;;
+ *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;;
+ esac
+done
+
+[[ "$(uname -s)" == "Darwin" ]] || { echo "ERROR: release.sh must run on macOS." >&2; exit 2; }
+for tool in xcodebuild xcrun hdiutil codesign spctl plutil shasum ditto; do
+ command -v "$tool" >/dev/null || { echo "ERROR: Missing required tool: $tool" >&2; exit 2; }
+done
+
+PBX="$PROJECT/project.pbxproj"
+VERSION="$(awk -F' = ' '/MARKETING_VERSION = / {gsub(/;/,"",$2); print $2; exit}' "$PBX")"
+BUILD="$(awk -F' = ' '/CURRENT_PROJECT_VERSION = / {gsub(/;/,"",$2); print $2; exit}' "$PBX")"
+[[ -n "$VERSION" && -n "$BUILD" ]] || { echo "ERROR: Could not determine version/build from Xcode project." >&2; exit 1; }
+
+RELEASE_DIR="$RELEASE_ROOT/$VERSION-$BUILD"
+ARCHIVE="$RELEASE_DIR/$PRODUCT_NAME.xcarchive"
+EXPORT_DIR="$RELEASE_DIR/export"
+DMG_STAGING="$RELEASE_DIR/dmg-root"
+DMG="$RELEASE_DIR/$PRODUCT_NAME-$VERSION.dmg"
+CHECKSUM="$DMG.sha256"
+METADATA="$RELEASE_DIR/release-info.txt"
+EXPORT_OPTIONS="$RELEASE_DIR/ExportOptions.plist"
+
+if [[ -e "$RELEASE_DIR" ]]; then
+ echo "ERROR: Release directory already exists: $RELEASE_DIR" >&2
+ echo "Refusing to overwrite an existing version/build artifact." >&2
+ exit 1
+fi
+mkdir -p "$RELEASE_DIR"
+
+cleanup() {
+ rm -rf "$DMG_STAGING"
+}
+trap cleanup EXIT
+
+if [[ "$SKIP_PREFLIGHT" -eq 0 ]]; then
+ "$SCRIPT_DIR/release-preflight.sh"
+fi
+
+cat > "$EXPORT_OPTIONS" <
+
+
+
+ method
+ developer-id
+ signingStyle
+ automatic
+ stripSwiftSymbols
+
+
+
+PLIST
+plutil -lint "$EXPORT_OPTIONS" >/dev/null
+
+XCODE_SIGN_ARGS=()
+if [[ -n "${DEVELOPMENT_TEAM:-}" ]]; then
+ XCODE_SIGN_ARGS+=("DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM")
+fi
+
+echo "== Archive $DISPLAY_NAME $VERSION ($BUILD) =="
+xcodebuild \
+ -project "$PROJECT" \
+ -scheme "$SCHEME" \
+ -configuration "$CONFIGURATION" \
+ -destination 'generic/platform=macOS' \
+ -archivePath "$ARCHIVE" \
+ clean archive \
+ "${XCODE_SIGN_ARGS[@]}"
+
+echo "== Export Developer ID app =="
+xcodebuild \
+ -exportArchive \
+ -archivePath "$ARCHIVE" \
+ -exportPath "$EXPORT_DIR" \
+ -exportOptionsPlist "$EXPORT_OPTIONS" \
+ -allowProvisioningUpdates
+
+APP="$EXPORT_DIR/$PRODUCT_NAME.app"
+[[ -d "$APP" ]] || { echo "ERROR: Exported app not found: $APP" >&2; exit 1; }
+
+ACTUAL_BUNDLE_ID="$(plutil -extract CFBundleIdentifier raw -o - "$APP/Contents/Info.plist" 2>/dev/null || true)"
+[[ "$ACTUAL_BUNDLE_ID" == "$BUNDLE_ID" ]] || { echo "ERROR: Exported bundle id is '$ACTUAL_BUNDLE_ID', expected '$BUNDLE_ID'." >&2; exit 1; }
+"$SCRIPT_DIR/verify-signed-app.sh" "$APP"
+
+if [[ "$SKIP_NOTARIZATION" -eq 0 ]]; then
+ echo "== Validate notarization credentials =="
+ xcrun notarytool history --keychain-profile "$NOTARY_PROFILE" >/dev/null
+
+ echo "== Notarize exported app =="
+ APP_ZIP="$RELEASE_DIR/$PRODUCT_NAME-$VERSION-notary.zip"
+ ditto -c -k --keepParent "$APP" "$APP_ZIP"
+ xcrun notarytool submit "$APP_ZIP" --keychain-profile "$NOTARY_PROFILE" --wait
+ xcrun stapler staple "$APP"
+ xcrun stapler validate "$APP"
+ rm -f "$APP_ZIP"
+ "$SCRIPT_DIR/verify-signed-app.sh" "$APP"
+fi
+
+echo "== Create drag-to-Applications DMG =="
+mkdir -p "$DMG_STAGING"
+ditto "$APP" "$DMG_STAGING/$PRODUCT_NAME.app"
+ln -s /Applications "$DMG_STAGING/Applications"
+cat > "$DMG_STAGING/README.txt" </dev/null
+
+hdiutil verify "$DMG" >/dev/null
+
+if [[ "$SKIP_NOTARIZATION" -eq 0 ]]; then
+ echo "== Notarize final DMG =="
+ xcrun notarytool submit "$DMG" --keychain-profile "$NOTARY_PROFILE" --wait
+ xcrun stapler staple "$DMG"
+ xcrun stapler validate "$DMG"
+
+ echo "== Final Gatekeeper verification =="
+ "$SCRIPT_DIR/verify-signed-app.sh" "$APP"
+ spctl --assess --type open --context context:primary-signature --verbose=4 "$DMG"
+else
+ echo "WARNING: Notarization was skipped. This output is NOT ready for public distribution." >&2
+fi
+
+printf '%s %s\n' "$(shasum -a 256 "$DMG" | awk '{print $1}')" "$(basename "$DMG")" > "$CHECKSUM"
+
+DMG_SIZE="$(stat -f '%z' "$DMG")"
+DMG_SHA256="$(awk '{print $1}' "$CHECKSUM")"
+cat > "$METADATA" <&2; exit 1; fi
+BUILD_DIR="$ROOT_DIR/build/direct-distribution"
+ARCHIVE_PATH="$BUILD_DIR/$APP_NAME.xcarchive"
+EXPORT_DIR="$BUILD_DIR/export"
+EXPORT_OPTIONS="$BUILD_DIR/ExportOptions.plist"
+APP_PATH="$EXPORT_DIR/$APP_NAME.app"
+APP_ZIP="$BUILD_DIR/$APP_NAME-notarization.zip"
+DMG_STAGE="$BUILD_DIR/dmg-stage"
+RW_DMG="$BUILD_DIR/$APP_NAME-rw.dmg"
+TMP_DMG="$BUILD_DIR/$APP_NAME.dmg"
+RELEASE_DIR="$ROOT_DIR/releases"
+DMG_PATH="$RELEASE_DIR/$APP_NAME.dmg"
+MOUNT_POINT="$BUILD_DIR/dmg-mount"
-usage() {
- cat </dev/null || { echo "ERROR: Missing $tool" >&2; exit 1; }
+done
+if ! security find-identity -v -p codesigning | grep -F "$SIGNING_IDENTITY" >/dev/null; then
+ echo "ERROR: Developer ID identity not found: $SIGNING_IDENTITY" >&2; exit 1
+fi
+xcrun notarytool history --keychain-profile "$NOTARY_PROFILE" >/dev/null || {
+ echo "ERROR: Notarytool profile '$NOTARY_PROFILE' unavailable" >&2; exit 1;
}
-while [[ $# -gt 0 ]]; do
- case "$1" in
- --notary-profile) [[ $# -ge 2 ]] || { echo "Missing value for $1" >&2; exit 2; }; NOTARY_PROFILE="$2"; shift 2 ;;
- --output) [[ $# -ge 2 ]] || { echo "Missing value for $1" >&2; exit 2; }; RELEASE_ROOT="$2"; shift 2 ;;
- --skip-preflight) SKIP_PREFLIGHT=1; shift ;;
- --skip-notarization) SKIP_NOTARIZATION=1; shift ;;
- -h|--help) usage; exit 0 ;;
- *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;;
- esac
-done
-
-[[ "$(uname -s)" == "Darwin" ]] || { echo "ERROR: release.sh must run on macOS." >&2; exit 2; }
-for tool in xcodebuild xcrun hdiutil codesign spctl plutil shasum ditto; do
- command -v "$tool" >/dev/null || { echo "ERROR: Missing required tool: $tool" >&2; exit 2; }
-done
-
-PBX="$PROJECT/project.pbxproj"
-VERSION="$(awk -F' = ' '/MARKETING_VERSION = / {gsub(/;/,"",$2); print $2; exit}' "$PBX")"
-BUILD="$(awk -F' = ' '/CURRENT_PROJECT_VERSION = / {gsub(/;/,"",$2); print $2; exit}' "$PBX")"
-[[ -n "$VERSION" && -n "$BUILD" ]] || { echo "ERROR: Could not determine version/build from Xcode project." >&2; exit 1; }
-
-RELEASE_DIR="$RELEASE_ROOT/$VERSION-$BUILD"
-ARCHIVE="$RELEASE_DIR/$PRODUCT_NAME.xcarchive"
-EXPORT_DIR="$RELEASE_DIR/export"
-DMG_STAGING="$RELEASE_DIR/dmg-root"
-DMG="$RELEASE_DIR/$PRODUCT_NAME-$VERSION.dmg"
-CHECKSUM="$DMG.sha256"
-METADATA="$RELEASE_DIR/release-info.txt"
-EXPORT_OPTIONS="$RELEASE_DIR/ExportOptions.plist"
-
-if [[ -e "$RELEASE_DIR" ]]; then
- echo "ERROR: Release directory already exists: $RELEASE_DIR" >&2
- echo "Refusing to overwrite an existing version/build artifact." >&2
- exit 1
-fi
-mkdir -p "$RELEASE_DIR"
-
-cleanup() {
- rm -rf "$DMG_STAGING"
-}
-trap cleanup EXIT
-
-if [[ "$SKIP_PREFLIGHT" -eq 0 ]]; then
- "$SCRIPT_DIR/release-preflight.sh"
-fi
-
+cd "$ROOT_DIR"
+rm -rf "$BUILD_DIR"
+mkdir -p "$BUILD_DIR" "$EXPORT_DIR" "$DMG_STAGE" "$RELEASE_DIR" "$MOUNT_POINT"
cat > "$EXPORT_OPTIONS" <
-
-
- method
- developer-id
- signingStyle
- automatic
- stripSwiftSymbols
-
-
-
+
+methoddeveloper-id
+signingStyleautomatic
+teamID$TEAM_ID
+
PLIST
-plutil -lint "$EXPORT_OPTIONS" >/dev/null
-XCODE_SIGN_ARGS=()
-if [[ -n "${DEVELOPMENT_TEAM:-}" ]]; then
- XCODE_SIGN_ARGS+=("DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM")
+echo '==> Archiving DBSyn with hardened runtime...'
+xcodebuild -project "$PROJECT" -scheme "$SCHEME" -configuration Release \
+ -destination 'generic/platform=macOS' -archivePath "$ARCHIVE_PATH" \
+ DEVELOPMENT_TEAM="$TEAM_ID" CODE_SIGN_STYLE=Automatic \
+ ENABLE_HARDENED_RUNTIME=YES \
+ clean archive
+
+echo '==> Exporting Developer ID app...'
+xcodebuild -exportArchive -archivePath "$ARCHIVE_PATH" \
+ -exportPath "$EXPORT_DIR" -exportOptionsPlist "$EXPORT_OPTIONS"
+[[ -d "$APP_PATH" ]] || { echo "ERROR: Missing exported app" >&2; exit 1; }
+
+# Xcode's generated Info.plist can omit CFBundleIconFile even when the
+# build setting specifies it. Set it on the exported bundle BEFORE the
+# final Developer ID signature, so the signed app includes the icon key.
+ICON_PATH="$APP_PATH/Contents/Resources/DBSyn.icns"
+[[ -s "$ICON_PATH" ]] || { echo "ERROR: Missing bundled icon $ICON_PATH" >&2; exit 1; }
+PLIST_PATH="$APP_PATH/Contents/Info.plist"
+ICON_KEY="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIconFile' "$PLIST_PATH" 2>/dev/null || true)"
+if [[ "$ICON_KEY" != DBSyn && "$ICON_KEY" != DBSyn.icns ]]; then
+ echo "==> Setting CFBundleIconFile in exported app (before signing)..."
+ if /usr/libexec/PlistBuddy -c 'Print :CFBundleIconFile' "$PLIST_PATH" >/dev/null 2>&1; then
+ /usr/libexec/PlistBuddy -c 'Set :CFBundleIconFile DBSyn.icns' "$PLIST_PATH"
+ else
+ /usr/libexec/PlistBuddy -c 'Add :CFBundleIconFile string DBSyn.icns' "$PLIST_PATH"
+ fi
fi
+ICON_KEY="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIconFile' "$PLIST_PATH")"
+[[ "$ICON_KEY" == DBSyn || "$ICON_KEY" == DBSyn.icns ]] || {
+ echo "ERROR: CFBundleIconFile still incorrect: '$ICON_KEY'" >&2; exit 1;
+}
+echo "==> Application icon verified: $ICON_KEY"
+sips -g pixelWidth -g pixelHeight "$ICON_PATH"
-echo "== Archive $DISPLAY_NAME $VERSION ($BUILD) =="
-xcodebuild \
- -project "$PROJECT" \
- -scheme "$SCHEME" \
- -configuration "$CONFIGURATION" \
- -destination 'generic/platform=macOS' \
- -archivePath "$ARCHIVE" \
- clean archive \
- "${XCODE_SIGN_ARGS[@]}"
+# Export may omit hardened runtime; sign exported app explicitly.
+# Sign embedded code first if your project adds frameworks/helpers in future.
+echo '==> Developer ID signing with hardened runtime...'
+codesign --force --deep --options runtime --timestamp --sign "$SIGNING_IDENTITY" "$APP_PATH"
+codesign --verify --deep --strict --verbose=2 "$APP_PATH"
+SIGNATURE_INFO="$(codesign -dv --verbose=4 "$APP_PATH" 2>&1 || true)"
+printf '%s\n' "$SIGNATURE_INFO" | grep -E 'Identifier=|Authority=|TeamIdentifier=|flags=' || true
+[[ "$SIGNATURE_INFO" == *"TeamIdentifier=$TEAM_ID"* ]] || { echo 'ERROR: Wrong signing team' >&2; exit 1; }
+[[ "$SIGNATURE_INFO" == *'(runtime)'* ]] || { echo 'ERROR: Hardened runtime missing' >&2; exit 1; }
-echo "== Export Developer ID app =="
-xcodebuild \
- -exportArchive \
- -archivePath "$ARCHIVE" \
- -exportPath "$EXPORT_DIR" \
- -exportOptionsPlist "$EXPORT_OPTIONS" \
- -allowProvisioningUpdates
+echo '==> Notarizing and stapling app...'
+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"
-APP="$EXPORT_DIR/$PRODUCT_NAME.app"
-[[ -d "$APP" ]] || { echo "ERROR: Exported app not found: $APP" >&2; exit 1; }
+echo '==> Preparing writable DMG...'
+ditto "$APP_PATH" "$DMG_STAGE/$APP_NAME.app"
+ln -s /Applications "$DMG_STAGE/Applications"
+# Create writable image first: the custom volume icon must be set ON the
+# mounted filesystem, not on the staging directory.
+hdiutil create -size 100m -fs HFS+ -volname "$APP_NAME" -ov "$RW_DMG"
+MOUNTED=0
+cleanup() {
+ if [[ "$MOUNTED" == 1 ]]; then hdiutil detach "$MOUNT_POINT" -quiet || true; fi
+}
+trap cleanup EXIT
+hdiutil attach "$RW_DMG" -nobrowse -noverify -mountpoint "$MOUNT_POINT" -quiet
+MOUNTED=1
+ditto "$DMG_STAGE/$APP_NAME.app" "$MOUNT_POINT/$APP_NAME.app"
+ln -s /Applications "$MOUNT_POINT/Applications"
+cp "$ICON_PATH" "$MOUNT_POINT/.VolumeIcon.icns"
+xcrun SetFile -a C "$MOUNT_POINT"
+hdiutil detach "$MOUNT_POINT" -quiet
+MOUNTED=0
+hdiutil convert "$RW_DMG" -format UDZO -o "$TMP_DMG" -ov
-ACTUAL_BUNDLE_ID="$(plutil -extract CFBundleIdentifier raw -o - "$APP/Contents/Info.plist" 2>/dev/null || true)"
-[[ "$ACTUAL_BUNDLE_ID" == "$BUNDLE_ID" ]] || { echo "ERROR: Exported bundle id is '$ACTUAL_BUNDLE_ID', expected '$BUNDLE_ID'." >&2; exit 1; }
-"$SCRIPT_DIR/verify-signed-app.sh" "$APP"
+# A DMG *file* icon is different from its mounted volume icon. Use AppKit to
+# assign a Finder custom icon to the DMG file before signing/notarization.
+# This creates Finder metadata (custom icon resource/finder flag).
+ICON_SWIFT="$BUILD_DIR/set-dmg-file-icon.swift"
+cat > "$ICON_SWIFT" <<'SWIFT'
+import AppKit
+import Foundation
+let arguments = CommandLine.arguments
+precondition(arguments.count == 3, "Usage: icon.swift ")
+guard let icon = NSImage(contentsOfFile: arguments[1]) else {
+ fputs("ERROR: Cannot load ICNS image\n", stderr); exit(1)
+}
+guard NSWorkspace.shared.setIcon(icon, forFile: arguments[2], options: []) else {
+ fputs("ERROR: NSWorkspace could not set DMG Finder icon\n", stderr); exit(1)
+}
+print("Custom Finder icon assigned to DMG file")
+SWIFT
+echo '==> Assigning Finder icon to DMG file...'
+swift "$ICON_SWIFT" "$ICON_PATH" "$TMP_DMG"
-if [[ "$SKIP_NOTARIZATION" -eq 0 ]]; then
- echo "== Validate notarization credentials =="
- xcrun notarytool history --keychain-profile "$NOTARY_PROFILE" >/dev/null
+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"
- echo "== Notarize exported app =="
- APP_ZIP="$RELEASE_DIR/$PRODUCT_NAME-$VERSION-notary.zip"
- ditto -c -k --keepParent "$APP" "$APP_ZIP"
- xcrun notarytool submit "$APP_ZIP" --keychain-profile "$NOTARY_PROFILE" --wait
- xcrun stapler staple "$APP"
- xcrun stapler validate "$APP"
- rm -f "$APP_ZIP"
- "$SCRIPT_DIR/verify-signed-app.sh" "$APP"
-fi
-
-echo "== Create drag-to-Applications DMG =="
-mkdir -p "$DMG_STAGING"
-ditto "$APP" "$DMG_STAGING/$PRODUCT_NAME.app"
-ln -s /Applications "$DMG_STAGING/Applications"
-cat > "$DMG_STAGING/README.txt" </dev/null
-
-hdiutil verify "$DMG" >/dev/null
-
-if [[ "$SKIP_NOTARIZATION" -eq 0 ]]; then
- echo "== Notarize final DMG =="
- xcrun notarytool submit "$DMG" --keychain-profile "$NOTARY_PROFILE" --wait
- xcrun stapler staple "$DMG"
- xcrun stapler validate "$DMG"
-
- echo "== Final Gatekeeper verification =="
- "$SCRIPT_DIR/verify-signed-app.sh" "$APP"
- spctl --assess --type open --context context:primary-signature --verbose=4 "$DMG"
-else
- echo "WARNING: Notarization was skipped. This output is NOT ready for public distribution." >&2
-fi
-
-printf '%s %s\n' "$(shasum -a 256 "$DMG" | awk '{print $1}')" "$(basename "$DMG")" > "$CHECKSUM"
-
-DMG_SIZE="$(stat -f '%z' "$DMG")"
-DMG_SHA256="$(awk '{print $1}' "$CHECKSUM")"
-cat > "$METADATA" < Release ready:'
+ls -lh "$DMG_PATH"
+shasum -a 256 "$DMG_PATH"
+printf '\n========== DBSyn RELEASE COMPLETE ==========\n%s\n' "$DMG_PATH"