feat(macos): сборка DMG, фикс keychain и opus-библиотек
- secure storage: usesDataProtectionKeychain=false — вход в аккаунт больше не падает с -34018 без аккаунта разработчика - entitlements: убран app-sandbox/keychain-access-groups (adhoc без Team ID) - иконка: squircle с прозрачными полями (нативный macOS-вид) - opus/ogg: битые bitcode-библиотеки ogg_opus_player заменены на собранные из исходников universal (.a в macos/prebuilt/opus + scripts/fix_opus_macos.sh применяет их после pub get) - scripts/build_dmg.sh: adhoc-сборка universal DMG с дизайном (create-dmg) - CI: build-macos.yml собирает DMG-артефакт; flutter-main.yml чинит libs Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@@ -35,22 +35,16 @@ jobs:
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
channel: 'stable'
|
||||
|
||||
- name: Get dependencies
|
||||
run: flutter pub get
|
||||
- name: Install create-dmg
|
||||
run: brew install create-dmg
|
||||
|
||||
- name: Build macOS
|
||||
run: flutter build macos --release
|
||||
- name: Build signed DMG
|
||||
run: ./scripts/build_dmg.sh
|
||||
|
||||
- name: Package macOS
|
||||
run: |
|
||||
mkdir -p dist
|
||||
APP=$(find build/macos/Build/Products/Release -maxdepth 1 -name "*.app" | head -1)
|
||||
ditto -c -k --keepParent "$APP" dist/Komet-macos.zip
|
||||
|
||||
- name: Upload macOS artifact
|
||||
- name: Upload macOS DMG artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: komet-macos
|
||||
path: dist/Komet-macos.zip
|
||||
path: dist/*.dmg
|
||||
if-no-files-found: error
|
||||
retention-days: 30
|
||||
|
||||
@@ -121,5 +121,8 @@ jobs:
|
||||
- name: Build iOS
|
||||
run: flutter build ios --release --no-codesign
|
||||
|
||||
- name: Fix ogg_opus_player macOS libs
|
||||
run: ./scripts/fix_opus_macos.sh
|
||||
|
||||
- name: Build macOS
|
||||
run: flutter build macos --release
|
||||
|
||||
@@ -50,6 +50,7 @@ analysis_benchmark.json
|
||||
.pub-preload-cache/
|
||||
.pub/
|
||||
build/
|
||||
dist/
|
||||
flutter_*.png
|
||||
linked_*.ds
|
||||
unlinked.ds
|
||||
|
||||
@@ -7,6 +7,7 @@ class TokenStorage {
|
||||
|
||||
static const _secure = FlutterSecureStorage(
|
||||
aOptions: AndroidOptions(encryptedSharedPreferences: true),
|
||||
mOptions: MacOsOptions(usesDataProtectionKeychain: false),
|
||||
);
|
||||
|
||||
static Future<void> writeSecure(String key, String value) async {
|
||||
|
||||
@@ -1,54 +1,258 @@
|
||||
PODS:
|
||||
- app_links (6.4.1):
|
||||
- FlutterMacOS
|
||||
- device_info_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
- dynamic_color (0.0.2):
|
||||
- FlutterMacOS
|
||||
- file_picker (0.0.1):
|
||||
- FlutterMacOS
|
||||
- Firebase/CoreOnly (12.15.0):
|
||||
- FirebaseCore (~> 12.15.0)
|
||||
- Firebase/Messaging (12.15.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseMessaging (~> 12.15.0)
|
||||
- firebase_core (4.11.0):
|
||||
- Firebase/CoreOnly (~> 12.15.0)
|
||||
- FlutterMacOS
|
||||
- firebase_messaging (16.4.1):
|
||||
- Firebase/CoreOnly (~> 12.15.0)
|
||||
- Firebase/Messaging (~> 12.15.0)
|
||||
- firebase_core
|
||||
- FlutterMacOS
|
||||
- FirebaseCore (12.15.0):
|
||||
- FirebaseCoreInternal (~> 12.15.0)
|
||||
- GoogleUtilities/Environment (~> 8.1)
|
||||
- GoogleUtilities/Logger (~> 8.1)
|
||||
- FirebaseCoreInternal (12.15.0):
|
||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||
- FirebaseInstallations (12.15.0):
|
||||
- FirebaseCore (~> 12.15.0)
|
||||
- GoogleUtilities/Environment (~> 8.1)
|
||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||
- PromisesObjC (~> 2.4)
|
||||
- FirebaseMessaging (12.15.0):
|
||||
- FirebaseCore (~> 12.15.0)
|
||||
- FirebaseInstallations (~> 12.15.0)
|
||||
- GoogleDataTransport (~> 10.1)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||
- GoogleUtilities/Environment (~> 8.1)
|
||||
- GoogleUtilities/Reachability (~> 8.1)
|
||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||
- nanopb (~> 3.30910.0)
|
||||
- flutter_inappwebview_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- OrderedSet (~> 6.0.3)
|
||||
- flutter_local_notifications (0.0.1):
|
||||
- FlutterMacOS
|
||||
- flutter_secure_storage_darwin (10.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- flutter_timezone (0.1.0):
|
||||
- FlutterMacOS
|
||||
- flutter_webrtc (1.4.0):
|
||||
- FlutterMacOS
|
||||
- WebRTC-SDK (= 144.7559.09)
|
||||
- FlutterMacOS (1.0.0)
|
||||
- geolocator_apple (1.2.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- GoogleDataTransport (10.1.0):
|
||||
- nanopb (~> 3.30910.0)
|
||||
- PromisesObjC (~> 2.4)
|
||||
- GoogleUtilities/AppDelegateSwizzler (8.1.1):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Network
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/Environment (8.1.1):
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/Logger (8.1.1):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/Network (8.1.1):
|
||||
- GoogleUtilities/Logger
|
||||
- "GoogleUtilities/NSData+zlib"
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/Reachability
|
||||
- "GoogleUtilities/NSData+zlib (8.1.1)":
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/Privacy (8.1.1)
|
||||
- GoogleUtilities/Reachability (8.1.1):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/UserDefaults (8.1.1):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Privacy
|
||||
- media_kit_libs_macos_video (1.0.4):
|
||||
- FlutterMacOS
|
||||
- media_kit_video (0.0.1):
|
||||
- FlutterMacOS
|
||||
- mobile_scanner (7.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- nanopb (3.30910.0):
|
||||
- nanopb/decode (= 3.30910.0)
|
||||
- nanopb/encode (= 3.30910.0)
|
||||
- nanopb/decode (3.30910.0)
|
||||
- nanopb/encode (3.30910.0)
|
||||
- ogg_opus_player (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- OrderedSet (6.0.3)
|
||||
- package_info_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
- photo_manager (3.9.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- PromisesObjC (2.4.1)
|
||||
- record_macos (1.2.1):
|
||||
- FlutterMacOS
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- sqflite_darwin (0.0.4):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- url_launcher_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- video_player_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- wakelock_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
- WebRTC-SDK (144.7559.09)
|
||||
|
||||
DEPENDENCIES:
|
||||
- app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
|
||||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||
- dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`)
|
||||
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
|
||||
- firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`)
|
||||
- firebase_messaging (from `Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos`)
|
||||
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
||||
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
|
||||
- flutter_secure_storage_darwin (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_darwin/darwin`)
|
||||
- flutter_timezone (from `Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos`)
|
||||
- flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/darwin`)
|
||||
- media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`)
|
||||
- media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`)
|
||||
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin`)
|
||||
- ogg_opus_player (from `Flutter/ephemeral/.symlinks/plugins/ogg_opus_player/darwin`)
|
||||
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||
- photo_manager (from `Flutter/ephemeral/.symlinks/plugins/photo_manager/darwin`)
|
||||
- record_macos (from `Flutter/ephemeral/.symlinks/plugins/record_macos/macos`)
|
||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)
|
||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||
- video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`)
|
||||
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- Firebase
|
||||
- FirebaseCore
|
||||
- FirebaseCoreInternal
|
||||
- FirebaseInstallations
|
||||
- FirebaseMessaging
|
||||
- GoogleDataTransport
|
||||
- GoogleUtilities
|
||||
- nanopb
|
||||
- OrderedSet
|
||||
- PromisesObjC
|
||||
- WebRTC-SDK
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
app_links:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/app_links/macos
|
||||
device_info_plus:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
|
||||
dynamic_color:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos
|
||||
file_picker:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
|
||||
firebase_core:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/firebase_core/macos
|
||||
firebase_messaging:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos
|
||||
flutter_inappwebview_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos
|
||||
flutter_local_notifications:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos
|
||||
flutter_secure_storage_darwin:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_darwin/darwin
|
||||
flutter_timezone:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_timezone/macos
|
||||
flutter_webrtc:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
geolocator_apple:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/darwin
|
||||
media_kit_libs_macos_video:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos
|
||||
media_kit_video:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos
|
||||
mobile_scanner:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin
|
||||
ogg_opus_player:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/ogg_opus_player/darwin
|
||||
package_info_plus:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
||||
photo_manager:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/photo_manager/darwin
|
||||
record_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/record_macos/macos
|
||||
shared_preferences_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
||||
sqflite_darwin:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin
|
||||
url_launcher_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||
video_player_avfoundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin
|
||||
wakelock_plus:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215
|
||||
dynamic_color: 5fdff3953fb3457311091863f72914fc76ea3209
|
||||
flutter_secure_storage_darwin: 557817588b80e60213cbecb573c45c76b788018d
|
||||
flutter_timezone: b3bc0c587d8780d395651284a1ff46eb1e5753ac
|
||||
app_links: 05a6ec2341985eb05e9f97dc63f5837c39895c3f
|
||||
device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
|
||||
dynamic_color: cb7c2a300ee67ed3bd96c3e852df3af0300bf610
|
||||
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
||||
Firebase: a8539b633d474fbeb654c7043f9c1649e274045b
|
||||
firebase_core: 4922bf7ac16c97fb67008a1a00842059b345b0d9
|
||||
firebase_messaging: 6c506190a7499e67e644957961f86efe1ac1c3e3
|
||||
FirebaseCore: 2e86a4ea1684d4381707069e4a6d89ac808e901e
|
||||
FirebaseCoreInternal: 6ab6a02c94446c026d2cf35cf5383842ebaa4992
|
||||
FirebaseInstallations: eb29ccbf64eaedf86fd5b2ccc7fabde567660b52
|
||||
FirebaseMessaging: 40017d7bc8457ee295b0f41d480a80fdabc9994e
|
||||
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
|
||||
flutter_local_notifications: 1fc7ffb10a83d6a2eeeeddb152d43f1944b0aad0
|
||||
flutter_secure_storage_darwin: acdb3f316ed05a3e68f856e0353b133eec373a23
|
||||
flutter_timezone: d272288c69082ad571630e0d17140b3d6b93dc0c
|
||||
flutter_webrtc: fec3dc9284a1f6d21ec2caa91c90bb4c1824eb04
|
||||
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
||||
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
|
||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
||||
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||
GoogleUtilities: 4f2618a4a1e762a1ee134a1e2323bba9843e06da
|
||||
media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65
|
||||
media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758
|
||||
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
|
||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||
ogg_opus_player: 954784304f4e2722780018c4abf284e4f93cddf5
|
||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
|
||||
photo_manager: 25fd77df14f4f0ba5ef99e2c61814dde77e2bceb
|
||||
PromisesObjC: 752c3227f599e3467650e47ea36f433eeb10c273
|
||||
record_macos: 5d55909f9650314be6424ffd6b123ac75a08c3c1
|
||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd
|
||||
video_player_avfoundation: 3453f792138786248960ca029747fcd9f318ef52
|
||||
wakelock_plus: 917609be14d812ddd9e9528876538b2263aaa03b
|
||||
WebRTC-SDK: e6006119cd730d6315d875e4a421b6cc8bb88833
|
||||
|
||||
PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@
|
||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||
AAC6FCD26EB13D7261C78A3C /* [CP] Embed Pods Frameworks */,
|
||||
BF4E11513C18E180EA08A66C /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -400,6 +401,23 @@
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
BF4E11513C18E180EA08A66C /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
C78DF3AD05984D1177E03977 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
||||
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 389 B After Width: | Height: | Size: 243 B |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 881 B After Width: | Height: | Size: 506 B |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -2,8 +2,6 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="Komet"
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
APP_PATH="$PROJECT_ROOT/build/macos/Build/Products/Release/$APP_NAME.app"
|
||||
|
||||
VERSION="$(grep '^version:' "$PROJECT_ROOT/pubspec.yaml" | sed 's/version: *//' | cut -d'+' -f1)"
|
||||
DIST_DIR="$PROJECT_ROOT/dist"
|
||||
DMG_PATH="$DIST_DIR/$APP_NAME-$VERSION.dmg"
|
||||
|
||||
echo "==> Building release macOS app"
|
||||
cd "$PROJECT_ROOT"
|
||||
flutter pub get
|
||||
"$PROJECT_ROOT/scripts/fix_opus_macos.sh"
|
||||
flutter build macos --release
|
||||
|
||||
if [[ ! -d "$APP_PATH" ]]; then
|
||||
echo "error: $APP_PATH not found after build" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Verifying ad-hoc signature (Xcode signs with CODE_SIGN_IDENTITY=- and Release.entitlements)"
|
||||
codesign --verify --deep --strict "$APP_PATH"
|
||||
if codesign -d --entitlements :- "$APP_PATH" 2>/dev/null | grep -q "com.apple.security.app-sandbox"; then
|
||||
echo "error: app-sandbox is enabled; flutter_secure_storage keychain will fail (-34018) without a Developer Team" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$DIST_DIR"
|
||||
rm -f "$DMG_PATH"
|
||||
|
||||
BACKGROUND="$PROJECT_ROOT/scripts/dmg/background.png"
|
||||
|
||||
if command -v create-dmg >/dev/null 2>&1; then
|
||||
echo "==> Packaging with create-dmg"
|
||||
create-dmg \
|
||||
--volname "$APP_NAME $VERSION" \
|
||||
--background "$BACKGROUND" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 640 400 \
|
||||
--icon-size 128 \
|
||||
--text-size 13 \
|
||||
--icon "$APP_NAME.app" 170 190 \
|
||||
--app-drop-link 470 190 \
|
||||
--hide-extension "$APP_NAME.app" \
|
||||
--no-internet-enable \
|
||||
"$DMG_PATH" \
|
||||
"$APP_PATH"
|
||||
else
|
||||
echo "==> create-dmg not found, packaging with hdiutil"
|
||||
STAGING="$(mktemp -d)"
|
||||
cp -R "$APP_PATH" "$STAGING/"
|
||||
ln -s /Applications "$STAGING/Applications"
|
||||
hdiutil create \
|
||||
-volname "$APP_NAME $VERSION" \
|
||||
-srcfolder "$STAGING" \
|
||||
-ov -format UDZO \
|
||||
"$DMG_PATH"
|
||||
rm -rf "$STAGING"
|
||||
fi
|
||||
|
||||
echo "==> Done: $DMG_PATH"
|
||||
|
After Width: | Height: | Size: 6.1 KiB |
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Replaces the broken bitcode-only opus/ogg static libraries shipped by
|
||||
# ogg_opus_player with real machine-code universal libraries vendored in
|
||||
# macos/prebuilt/opus. Run after `flutter pub get`, before building macOS.
|
||||
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SRC="$PROJECT_ROOT/macos/prebuilt/opus"
|
||||
|
||||
PKG_CFG="$PROJECT_ROOT/.dart_tool/package_config.json"
|
||||
LIBS=""
|
||||
|
||||
if [ -f "$PKG_CFG" ] && command -v python3 >/dev/null 2>&1; then
|
||||
ROOT="$(python3 - "$PKG_CFG" <<'PY'
|
||||
import json, sys, urllib.parse
|
||||
data = json.load(open(sys.argv[1]))
|
||||
for pkg in data.get("packages", []):
|
||||
if pkg["name"] == "ogg_opus_player":
|
||||
print(urllib.parse.unquote(urllib.parse.urlparse(pkg["rootUri"]).path))
|
||||
break
|
||||
PY
|
||||
)"
|
||||
if [ -n "$ROOT" ]; then
|
||||
case "$ROOT" in
|
||||
/*) PKGDIR="$ROOT" ;;
|
||||
*) PKGDIR="$(cd "$PROJECT_ROOT/.dart_tool/$ROOT" && pwd)" ;;
|
||||
esac
|
||||
LIBS="$PKGDIR/darwin/Libs"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$LIBS" ] || [ ! -d "$LIBS" ]; then
|
||||
for cand in "${PUB_CACHE:-$HOME/.pub-cache}/hosted"/*/ogg_opus_player-*/darwin/Libs; do
|
||||
[ -d "$cand" ] && LIBS="$cand" && break
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -z "$LIBS" ] || [ ! -d "$LIBS" ]; then
|
||||
echo "warning: ogg_opus_player macOS Libs dir not found; skipping opus fix" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "==> Installing prebuilt opus libs into: $LIBS"
|
||||
for f in libogg.a libopus.a libopusfile.a libopusenc.a; do
|
||||
cp -f "$SRC/$f" "$LIBS/$f"
|
||||
echo " installed $f ($(lipo -archs "$LIBS/$f" 2>/dev/null))"
|
||||
done
|
||||
echo "==> opus libs fixed"
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'dart:io';
|
||||
import 'package:image/image.dart' as img;
|
||||
|
||||
void main() {
|
||||
const w = 1280;
|
||||
const h = 800;
|
||||
final bg = img.Image(width: w, height: h, numChannels: 4);
|
||||
|
||||
final topR = 251, topG = 251, topB = 253;
|
||||
final botR = 233, botG = 233, botB = 237;
|
||||
for (var y = 0; y < h; y++) {
|
||||
final t = y / (h - 1);
|
||||
final r = (topR + (botR - topR) * t).round();
|
||||
final g = (topG + (botG - topG) * t).round();
|
||||
final b = (topB + (botB - topB) * t).round();
|
||||
img.fillRect(bg, x1: 0, y1: y, x2: w - 1, y2: y, color: img.ColorRgb8(r, g, b));
|
||||
}
|
||||
|
||||
final green = img.ColorRgb8(124, 191, 64);
|
||||
const ay = 380;
|
||||
img.fillRect(bg, x1: 548, y1: ay - 9, x2: 704, y2: ay + 9, color: green);
|
||||
img.fillPolygon(bg, vertices: [
|
||||
img.Point(700, ay - 30),
|
||||
img.Point(700, ay + 30),
|
||||
img.Point(752, ay),
|
||||
], color: green);
|
||||
|
||||
final outDir = Directory('scripts/dmg');
|
||||
outDir.createSync(recursive: true);
|
||||
File('scripts/dmg/background.png').writeAsBytesSync(img.encodePng(bg));
|
||||
stdout.writeln('wrote scripts/dmg/background.png (${w}x$h)');
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
import 'package:image/image.dart' as img;
|
||||
|
||||
const _dir = 'macos/Runner/Assets.xcassets/AppIcon.appiconset';
|
||||
|
||||
const _sizes = {
|
||||
'app_icon_16.png': 16,
|
||||
'app_icon_32.png': 32,
|
||||
'app_icon_64.png': 64,
|
||||
'app_icon_128.png': 128,
|
||||
'app_icon_256.png': 256,
|
||||
'app_icon_512.png': 512,
|
||||
'app_icon_1024.png': 1024,
|
||||
};
|
||||
|
||||
void main() {
|
||||
final source = img.decodePng(File('assets/meteor_icon.png').readAsBytesSync()) ??
|
||||
img.decodePng(File('$_dir/app_icon_1024.png').readAsBytesSync())!;
|
||||
|
||||
const canvas = 1024;
|
||||
const content = 824;
|
||||
const margin = (canvas - content) / 2;
|
||||
const radius = 184.0;
|
||||
|
||||
final body = img.copyResize(source,
|
||||
width: content, height: content, interpolation: img.Interpolation.cubic);
|
||||
|
||||
final master = img.Image(width: canvas, height: canvas, numChannels: 4);
|
||||
img.compositeImage(master, body, dstX: margin.toInt(), dstY: margin.toInt());
|
||||
|
||||
_applyRoundedMask(master, margin, margin, margin + content, margin + content, radius);
|
||||
|
||||
for (final entry in _sizes.entries) {
|
||||
final out = img.copyResize(master,
|
||||
width: entry.value,
|
||||
height: entry.value,
|
||||
interpolation: img.Interpolation.cubic);
|
||||
File('$_dir/${entry.key}').writeAsBytesSync(img.encodePng(out));
|
||||
stdout.writeln('wrote ${entry.key} (${entry.value}x${entry.value})');
|
||||
}
|
||||
}
|
||||
|
||||
void _applyRoundedMask(
|
||||
img.Image im, double x0, double y0, double x1, double y1, double r) {
|
||||
final cx = (x0 + x1) / 2;
|
||||
final cy = (y0 + y1) / 2;
|
||||
final hw = (x1 - x0) / 2;
|
||||
final hh = (y1 - y0) / 2;
|
||||
for (final p in im) {
|
||||
final qx = (p.x + 0.5 - cx).abs() - (hw - r);
|
||||
final qy = (p.y + 0.5 - cy).abs() - (hh - r);
|
||||
final dx = math.max(qx, 0.0);
|
||||
final dy = math.max(qy, 0.0);
|
||||
final dist =
|
||||
math.sqrt(dx * dx + dy * dy) + math.min(math.max(qx, qy), 0.0) - r;
|
||||
final coverage = (0.5 - dist).clamp(0.0, 1.0);
|
||||
if (coverage < 1.0) {
|
||||
p.a = (p.a * coverage).round().clamp(0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||