WIP
This commit is contained in:
49
scripts/build-macos-app.sh
Executable file
49
scripts/build-macos-app.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="ClipBored"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
OUTPUT_ROOT="$REPO_ROOT/build"
|
||||
APP_BUNDLE="$OUTPUT_ROOT/${APP_NAME}.app"
|
||||
BIN_NAME="$APP_NAME"
|
||||
BIN_PATH="$REPO_ROOT/.build/release/$BIN_NAME"
|
||||
INFO_PLIST="$REPO_ROOT/sources/clipbored/resources/Info.plist"
|
||||
ICON_FILE="$REPO_ROOT/sources/clipbored/resources/AppIcon.icns"
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
swift build -c release --product "$APP_NAME" \
|
||||
-Xswiftc -Osize \
|
||||
-Xswiftc -whole-module-optimization \
|
||||
-Xswiftc -gnone \
|
||||
-Xswiftc -Xfrontend \
|
||||
-Xswiftc -disable-reflection-metadata \
|
||||
-Xlinker -dead_strip \
|
||||
-Xlinker -no_function_starts
|
||||
rm -rf "$APP_BUNDLE"
|
||||
mkdir -p "$APP_BUNDLE/Contents/MacOS" "$APP_BUNDLE/Contents/Resources"
|
||||
cp "$INFO_PLIST" "$APP_BUNDLE/Contents/Info.plist"
|
||||
cp "$ICON_FILE" "$APP_BUNDLE/Contents/Resources/AppIcon.icns"
|
||||
cp "$BIN_PATH" "$APP_BUNDLE/Contents/MacOS/$APP_NAME"
|
||||
chmod +x "$APP_BUNDLE/Contents/MacOS/$APP_NAME"
|
||||
strip "$APP_BUNDLE/Contents/MacOS/$APP_NAME"
|
||||
codesign --deep --force --options runtime --sign - "$APP_BUNDLE" >/dev/null 2>&1 || true
|
||||
touch "$APP_BUNDLE"
|
||||
|
||||
APP_SIZE=$(stat -f%z "$APP_BUNDLE/Contents/MacOS/$APP_NAME")
|
||||
APP_SIZE_LIMIT=$((1024 * 1024))
|
||||
HUMAN_SIZE=$(du -h "$APP_BUNDLE/Contents/MacOS/$APP_NAME" | cut -f1)
|
||||
APP_BUNDLE_SIZE=$(du -sh "$APP_BUNDLE" | cut -f1)
|
||||
APP_BUNDLE_BYTES=$(du -sk "$APP_BUNDLE" | awk '{print $1*1024}')
|
||||
echo "Built $APP_BUNDLE"
|
||||
echo "Binary size: $HUMAN_SIZE ($APP_SIZE bytes)"
|
||||
echo "Bundle size: $APP_BUNDLE_SIZE"
|
||||
if [ "$APP_SIZE" -gt "$APP_SIZE_LIMIT" ]; then
|
||||
echo "FAIL: executable exceeds 1MiB target ($APP_SIZE bytes)"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$APP_BUNDLE_BYTES" -gt 1800000 ]; then
|
||||
echo "FAIL: bundle exceeds 1.8MB target ($APP_BUNDLE_BYTES bytes)"
|
||||
exit 1
|
||||
fi
|
||||
12
scripts/check.sh
Executable file
12
scripts/check.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
bash -n "$SCRIPT_DIR/release-macos-app.sh"
|
||||
swift test -q
|
||||
"$SCRIPT_DIR/build-macos-app.sh"
|
||||
codesign --verify --deep --strict --verbose=2 "$REPO_ROOT/build/ClipBored.app"
|
||||
22
scripts/idle-soak-report.sh
Executable file
22
scripts/idle-soak-report.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="ClipBored"
|
||||
DURATION_SECONDS="${1:-900}"
|
||||
|
||||
PID="$(pgrep -x "$APP_NAME" | head -n 1 || true)"
|
||||
if [ -z "$PID" ]; then
|
||||
echo "ClipBored is not running. Launch build/ClipBored.app first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Idle soak for $APP_NAME (pid $PID)"
|
||||
echo "Duration: ${DURATION_SECONDS}s"
|
||||
echo "Start:"
|
||||
ps -o pid,pcpu,pmem,time,command -p "$PID"
|
||||
|
||||
sleep "$DURATION_SECONDS"
|
||||
|
||||
echo "End:"
|
||||
ps -o pid,pcpu,pmem,time,command -p "$PID"
|
||||
echo "Use Instruments or Activity Monitor Energy tab for wakeup/energy validation."
|
||||
102
scripts/release-macos-app.sh
Executable file
102
scripts/release-macos-app.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
APP_NAME="ClipBored"
|
||||
APP_BUNDLE="$REPO_ROOT/build/${APP_NAME}.app"
|
||||
ZIP_PATH="$REPO_ROOT/build/${APP_NAME}.zip"
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/release-macos-app.sh
|
||||
|
||||
Builds build/ClipBored.app, optionally re-signs it with a Developer ID
|
||||
Application certificate, optionally notarizes it, staples the ticket, and
|
||||
creates build/ClipBored.zip.
|
||||
|
||||
Optional environment:
|
||||
DEVELOPER_ID_APPLICATION codesign identity, e.g. "Developer ID Application: Example, Inc. (TEAMID)"
|
||||
NOTARYTOOL_PROFILE preferred notarytool keychain profile
|
||||
|
||||
Alternative notarization credentials when NOTARYTOOL_PROFILE is not set:
|
||||
APPLE_ID Apple ID email
|
||||
APPLE_TEAM_ID Apple Developer Team ID
|
||||
APPLE_APP_SPECIFIC_PASSWORD app-specific password
|
||||
|
||||
Without DEVELOPER_ID_APPLICATION, this script performs a local ad-hoc signed
|
||||
release build and zip only. Notarization is skipped.
|
||||
USAGE
|
||||
}
|
||||
|
||||
create_zip_archive() {
|
||||
rm -f "$ZIP_PATH"
|
||||
(
|
||||
cd "$REPO_ROOT/build"
|
||||
/usr/bin/zip -qry --symlinks "$ZIP_PATH" "${APP_NAME}.app"
|
||||
)
|
||||
local archive_list
|
||||
archive_list="$(/usr/bin/unzip -l "$ZIP_PATH")"
|
||||
if [[ "$archive_list" == *"__MACOSX"* || "$archive_list" == *"/._"* ]]; then
|
||||
echo "FAIL: release archive contains macOS metadata sidecar files."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
"$SCRIPT_DIR/build-macos-app.sh"
|
||||
|
||||
if [[ -n "${DEVELOPER_ID_APPLICATION:-}" ]]; then
|
||||
echo "Signing with Developer ID identity: $DEVELOPER_ID_APPLICATION"
|
||||
codesign \
|
||||
--deep \
|
||||
--force \
|
||||
--options runtime \
|
||||
--timestamp \
|
||||
--sign "$DEVELOPER_ID_APPLICATION" \
|
||||
"$APP_BUNDLE"
|
||||
else
|
||||
echo "DEVELOPER_ID_APPLICATION is not set; keeping local ad-hoc signature."
|
||||
fi
|
||||
|
||||
codesign --verify --deep --strict --verbose=2 "$APP_BUNDLE"
|
||||
SIGNATURE_DETAILS="$(codesign -d --verbose=4 "$APP_BUNDLE" 2>&1)"
|
||||
if [[ "$SIGNATURE_DETAILS" != *"runtime"* ]]; then
|
||||
echo "FAIL: hardened runtime was not found in the code signature."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
create_zip_archive
|
||||
echo "Created $ZIP_PATH"
|
||||
|
||||
if [[ -z "${DEVELOPER_ID_APPLICATION:-}" ]]; then
|
||||
echo "Skipping notarization because Developer ID signing is not configured."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -n "${NOTARYTOOL_PROFILE:-}" ]]; then
|
||||
echo "Submitting notarization with keychain profile: $NOTARYTOOL_PROFILE"
|
||||
xcrun notarytool submit "$ZIP_PATH" --keychain-profile "$NOTARYTOOL_PROFILE" --wait
|
||||
elif [[ -n "${APPLE_ID:-}" && -n "${APPLE_TEAM_ID:-}" && -n "${APPLE_APP_SPECIFIC_PASSWORD:-}" ]]; then
|
||||
echo "Submitting notarization with Apple ID credentials for team: $APPLE_TEAM_ID"
|
||||
xcrun notarytool submit "$ZIP_PATH" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--team-id "$APPLE_TEAM_ID" \
|
||||
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
|
||||
--wait
|
||||
else
|
||||
echo "Notarization credentials are not configured; signed zip is ready but not notarized."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
xcrun stapler staple "$APP_BUNDLE"
|
||||
xcrun stapler validate "$APP_BUNDLE"
|
||||
|
||||
create_zip_archive
|
||||
echo "Created notarized release archive: $ZIP_PATH"
|
||||
Reference in New Issue
Block a user