general: appstore build stuff
This commit is contained in:
10
.distribution_configs/appstore/Gaze.entitlements
Normal file
10
.distribution_configs/appstore/Gaze.entitlements
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?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>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
26
.distribution_configs/appstore/Info.plist
Normal file
26
.distribution_configs/appstore/Info.plist
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?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>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>LSUIElement</key>
|
||||||
|
<true/>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.productivity</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>Gaze</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Gaze</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright © 2026 Mike Freno. All rights reserved.</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
36
.distribution_configs/appstore/project_release.txt
Normal file
36
.distribution_configs/appstore/project_release.txt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
27A21B5F2F0F69DD0018C4F3 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = Gaze;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 6;
|
||||||
|
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||||
|
ENABLE_APP_SANDBOX = YES;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
|
INFOPLIST_FILE = Gaze/Info.plist;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
MARKETING_VERSION = 0.4.0;
|
||||||
|
OTHER_SWIFT_FLAGS = "-D APPSTORE";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.Gaze;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
REGISTER_APP_GROUPS = YES;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
15
.distribution_configs/self/Gaze.entitlements
Normal file
15
.distribution_configs/self/Gaze.entitlements
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?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>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
|
||||||
|
<array>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spks</string>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spki</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
36
.distribution_configs/self/Info.plist
Normal file
36
.distribution_configs/self/Info.plist
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?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>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>LSUIElement</key>
|
||||||
|
<true/>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.productivity</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>Gaze</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Gaze</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright © 2026 Mike Freno. All rights reserved.</string>
|
||||||
|
<key>SUPublicEDKey</key>
|
||||||
|
<string>Z2RmohI1y2bgeGQQUDqO9F0HNF2AzFotOt8CwGB6VJM=</string>
|
||||||
|
<key>SUFeedURL</key>
|
||||||
|
<string>https://freno.me/api/Gaze/appcast.xml</string>
|
||||||
|
<key>SUEnableAutomaticChecks</key>
|
||||||
|
<true/>
|
||||||
|
<key>SUScheduledCheckInterval</key>
|
||||||
|
<integer>86400</integer>
|
||||||
|
<key>SUEnableInstallerLauncherService</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
35
.distribution_configs/self/project_release.txt
Normal file
35
.distribution_configs/self/project_release.txt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
27A21B5F2F0F69DD0018C4F3 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = Gaze;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 6;
|
||||||
|
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||||
|
ENABLE_APP_SANDBOX = YES;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
|
INFOPLIST_FILE = Gaze/Info.plist;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
MARKETING_VERSION = 0.4.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.Gaze;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
REGISTER_APP_GROUPS = YES;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
214
DISTRIBUTION.md
Normal file
214
DISTRIBUTION.md
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
# Gaze Distribution Guide
|
||||||
|
|
||||||
|
This guide explains how to build and distribute Gaze for both the Mac App Store and direct distribution with auto-updates.
|
||||||
|
|
||||||
|
## Distribution Methods
|
||||||
|
|
||||||
|
Gaze supports two distribution methods:
|
||||||
|
|
||||||
|
1. **Self-Distribution** (Direct Download) - Includes Sparkle for automatic updates
|
||||||
|
2. **Mac App Store** - Uses Apple's update mechanism, no Sparkle
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Switching Between Distributions
|
||||||
|
|
||||||
|
Use the `switch_to` script to configure the project for each distribution method:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For self-distribution with Sparkle auto-updates
|
||||||
|
./switch_to self
|
||||||
|
|
||||||
|
# For Mac App Store submission
|
||||||
|
./switch_to appstore
|
||||||
|
|
||||||
|
# Check current configuration
|
||||||
|
./switch_to status
|
||||||
|
```
|
||||||
|
|
||||||
|
### What Gets Changed
|
||||||
|
|
||||||
|
The `switch_to` script automatically manages:
|
||||||
|
|
||||||
|
**Self-Distribution Mode:**
|
||||||
|
- ✅ Adds Sparkle keys to `Info.plist` (SUPublicEDKey, SUFeedURL, etc.)
|
||||||
|
- ✅ Adds Sparkle entitlements for XPC services
|
||||||
|
- ✅ Removes `APPSTORE` compiler flag
|
||||||
|
- ✅ Enables UpdateManager with Sparkle framework
|
||||||
|
|
||||||
|
**App Store Mode:**
|
||||||
|
- ✅ Removes all Sparkle keys from `Info.plist`
|
||||||
|
- ✅ Removes Sparkle entitlements
|
||||||
|
- ✅ Adds `-D APPSTORE` compiler flag
|
||||||
|
- ✅ Disables Sparkle code at compile time
|
||||||
|
|
||||||
|
## Building for Self-Distribution
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Switch to self-distribution mode
|
||||||
|
./switch_to self
|
||||||
|
```
|
||||||
|
|
||||||
|
The script will:
|
||||||
|
- Prompt for version bump (major/minor/patch)
|
||||||
|
- Build and code sign with Developer ID
|
||||||
|
- Notarize the app with Apple
|
||||||
|
- Create a signed DMG
|
||||||
|
- Generate Sparkle appcast with EdDSA signature
|
||||||
|
- (Optional) Upload to S3 if credentials are configured
|
||||||
|
|
||||||
|
## Building for Mac App Store
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Switch to App Store mode
|
||||||
|
./switch_to appstore
|
||||||
|
|
||||||
|
# 2. Add Run Script Phase in Xcode (one-time setup)
|
||||||
|
# See section below
|
||||||
|
|
||||||
|
# 3. Archive and distribute via Xcode
|
||||||
|
# Product → Archive
|
||||||
|
# Window → Organizer → Distribute App → App Store Connect
|
||||||
|
```
|
||||||
|
|
||||||
|
### Required: Run Script Phase
|
||||||
|
|
||||||
|
For App Store builds, you **must** add this Run Script phase in Xcode:
|
||||||
|
|
||||||
|
1. Open Gaze.xcodeproj in Xcode
|
||||||
|
2. Select the Gaze target → Build Phases
|
||||||
|
3. Click + → New Run Script Phase
|
||||||
|
4. Name it: "Remove Sparkle for App Store"
|
||||||
|
5. Place it **after** "Embed Frameworks"
|
||||||
|
6. Add this script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
if [[ "${OTHER_SWIFT_FLAGS}" == *"APPSTORE"* ]]; then
|
||||||
|
echo "Removing Sparkle framework for App Store build..."
|
||||||
|
rm -rf "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sparkle.framework"
|
||||||
|
echo "Sparkle framework removed successfully"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures Sparkle.framework is removed from the app bundle before submission.
|
||||||
|
|
||||||
|
## Configuration Files
|
||||||
|
|
||||||
|
Configuration backups are stored in `.distribution_configs/`:
|
||||||
|
- `.distribution_configs/appstore/` - App Store configuration
|
||||||
|
- `.distribution_configs/self/` - Self-distribution configuration
|
||||||
|
|
||||||
|
These backups are created automatically and used when switching between modes.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
Before submitting to App Store Connect, verify your configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./switch_to status
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output for App Store:
|
||||||
|
```
|
||||||
|
Info.plist: App Store (no Sparkle keys)
|
||||||
|
Entitlements: App Store (no Sparkle exceptions)
|
||||||
|
Build Settings: App Store (has APPSTORE flag)
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output for Self-Distribution:
|
||||||
|
```
|
||||||
|
Info.plist: Self-Distribution (has Sparkle keys)
|
||||||
|
Entitlements: Self-Distribution (has Sparkle exceptions)
|
||||||
|
Build Settings: Self-Distribution (no APPSTORE flag)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### App Store Validation Fails
|
||||||
|
|
||||||
|
**Error: "App sandbox not enabled" with Sparkle executables**
|
||||||
|
- Solution: Make sure you ran `./switch_to appstore` and added the Run Script phase
|
||||||
|
|
||||||
|
**Error: "Bad Bundle Executable" or "CFBundlePackageType"**
|
||||||
|
- Solution: These are now fixed in the Info.plist
|
||||||
|
|
||||||
|
**Error: Still seeing Sparkle in the build**
|
||||||
|
- Solution: Clean build folder (⌘⇧K) and rebuild
|
||||||
|
|
||||||
|
### Self-Distribution Issues
|
||||||
|
|
||||||
|
**Sparkle updates not working**
|
||||||
|
- Verify: `./switch_to status` shows "Self-Distribution" mode
|
||||||
|
- Check: Info.plist contains SUPublicEDKey and SUFeedURL
|
||||||
|
- Verify: Appcast is accessible at the SUFeedURL
|
||||||
|
|
||||||
|
**Code signing issues**
|
||||||
|
- Check `.env` file has correct credentials
|
||||||
|
- Verify Developer ID certificate: `security find-identity -v -p codesigning`
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
For self-distribution, create a `.env` file with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Required for code signing
|
||||||
|
DEVELOPER_ID_APPLICATION="Developer ID Application: Your Name (TEAM_ID)"
|
||||||
|
APPLE_TEAM_ID="XXXXXXXXXX"
|
||||||
|
|
||||||
|
# Required for notarization
|
||||||
|
NOTARY_KEYCHAIN_PROFILE="notary-profile"
|
||||||
|
|
||||||
|
# Optional for S3 upload
|
||||||
|
AWS_ACCESS_KEY_ID="your-key"
|
||||||
|
AWS_SECRET_ACCESS_KEY="your-secret"
|
||||||
|
AWS_BUCKET_NAME="your-bucket"
|
||||||
|
AWS_REGION="us-east-1"
|
||||||
|
```
|
||||||
|
|
||||||
|
Setup notarization profile (one-time):
|
||||||
|
```bash
|
||||||
|
xcrun notarytool store-credentials "notary-profile" \
|
||||||
|
--apple-id "your@email.com" \
|
||||||
|
--team-id "TEAM_ID"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version Management
|
||||||
|
|
||||||
|
The `self_distribute` script handles version bumping:
|
||||||
|
- **Major** (X.0.0) - Breaking changes
|
||||||
|
- **Minor** (x.X.0) - New features
|
||||||
|
- **Patch** (x.x.X) - Bug fixes
|
||||||
|
- **Custom** - Any version string
|
||||||
|
- **Keep** - Increment build number only
|
||||||
|
|
||||||
|
Git tags are created automatically for new versions.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Test Self-Distribution Build
|
||||||
|
```bash
|
||||||
|
./switch_to self
|
||||||
|
# Test the DMG on a clean macOS system
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test App Store Build
|
||||||
|
```bash
|
||||||
|
./switch_to appstore
|
||||||
|
# Archive in Xcode
|
||||||
|
# Use TestFlight for testing before release
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Always use `switch_to`** - Don't manually edit configuration files
|
||||||
|
2. **Check status before building** - Use `./switch_to status`
|
||||||
|
3. **Clean builds** - Run Clean Build Folder when switching modes
|
||||||
|
4. **Test thoroughly** - Test both distribution methods separately
|
||||||
|
5. **Commit before switching** - Use git to track configuration changes
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues or questions:
|
||||||
|
- GitHub Issues: https://github.com/mikefreno/Gaze/issues
|
||||||
|
- Check AGENTS.md for development guidelines
|
||||||
@@ -121,6 +121,7 @@
|
|||||||
buildPhases = (
|
buildPhases = (
|
||||||
27A21B382F0F69DC0018C4F3 /* Sources */,
|
27A21B382F0F69DC0018C4F3 /* Sources */,
|
||||||
27A21B392F0F69DC0018C4F3 /* Frameworks */,
|
27A21B392F0F69DC0018C4F3 /* Frameworks */,
|
||||||
|
27D081082F16AA7100FF3A31 /* Run Script */,
|
||||||
27A21B3A2F0F69DC0018C4F3 /* Resources */,
|
27A21B3A2F0F69DC0018C4F3 /* Resources */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
@@ -257,6 +258,27 @@
|
|||||||
};
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
27D081082F16AA7100FF3A31 /* Run Script */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = " #!/bin/bash\n if [[ \"${OTHER_SWIFT_FLAGS}\" == *\"APPSTORE\"* ]]; then\n echo \"Removing Sparkle framework for App Store build...\"\n rm -rf \"${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sparkle.framework\"\n echo \"Sparkle framework removed successfully\"\n fi\n";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
27A21B382F0F69DC0018C4F3 /* Sources */ = {
|
27A21B382F0F69DC0018C4F3 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
@@ -475,6 +497,7 @@
|
|||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 0.4.0;
|
MARKETING_VERSION = 0.4.0;
|
||||||
|
OTHER_SWIFT_FLAGS = "-D APPSTORE";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.Gaze;
|
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.Gaze;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
|
|||||||
0
Gaze.xcodeproj/project.pbxproj.tmp
Normal file
0
Gaze.xcodeproj/project.pbxproj.tmp
Normal file
@@ -193,8 +193,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
window.backgroundColor = .clear
|
window.backgroundColor = .clear
|
||||||
window.contentView = NSHostingView(rootView: content)
|
window.contentView = NSHostingView(rootView: content)
|
||||||
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
|
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
|
||||||
window.acceptsMouseMovedEvents = !requiresFocus
|
|
||||||
window.ignoresMouseEvents = !requiresFocus
|
// Allow mouse events for all reminders (needed for dismiss button)
|
||||||
|
window.acceptsMouseMovedEvents = true
|
||||||
|
window.ignoresMouseEvents = false
|
||||||
|
|
||||||
let windowController = NSWindowController(window: window)
|
let windowController = NSWindowController(window: window)
|
||||||
windowController.showWindow(nil)
|
windowController.showWindow(nil)
|
||||||
|
|||||||
@@ -6,10 +6,5 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
|
|
||||||
<array>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spks</string>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spki</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -12,6 +12,15 @@ struct GazeApp: App {
|
|||||||
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||||
@StateObject private var settingsManager = SettingsManager.shared
|
@StateObject private var settingsManager = SettingsManager.shared
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// Handle test launch arguments
|
||||||
|
if TestingEnvironment.shouldSkipOnboarding {
|
||||||
|
SettingsManager.shared.settings.hasCompletedOnboarding = true
|
||||||
|
} else if TestingEnvironment.shouldResetOnboarding {
|
||||||
|
SettingsManager.shared.settings.hasCompletedOnboarding = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
// Onboarding window (only shown when not completed)
|
// Onboarding window (only shown when not completed)
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
<key>LSUIElement</key>
|
<key>LSUIElement</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
@@ -18,15 +22,5 @@
|
|||||||
<string>$(MARKETING_VERSION)</string>
|
<string>$(MARKETING_VERSION)</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2026 Mike Freno. All rights reserved.</string>
|
<string>Copyright © 2026 Mike Freno. All rights reserved.</string>
|
||||||
<key>SUPublicEDKey</key>
|
|
||||||
<string>Z2RmohI1y2bgeGQQUDqO9F0HNF2AzFotOt8CwGB6VJM=</string>
|
|
||||||
<key>SUFeedURL</key>
|
|
||||||
<string>https://freno.me/api/Gaze/appcast.xml</string>
|
|
||||||
<key>SUEnableAutomaticChecks</key>
|
|
||||||
<true/>
|
|
||||||
<key>SUScheduledCheckInterval</key>
|
|
||||||
<integer>86400</integer>
|
|
||||||
<key>SUEnableInstallerLauncherService</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -7,24 +7,31 @@
|
|||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
|
#if !APPSTORE
|
||||||
import Sparkle
|
import Sparkle
|
||||||
|
#endif
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class UpdateManager: NSObject, ObservableObject {
|
class UpdateManager: NSObject, ObservableObject {
|
||||||
static let shared = UpdateManager()
|
static let shared = UpdateManager()
|
||||||
|
|
||||||
|
#if !APPSTORE
|
||||||
private var updaterController: SPUStandardUpdaterController?
|
private var updaterController: SPUStandardUpdaterController?
|
||||||
private var automaticallyChecksObservation: NSKeyValueObservation?
|
private var automaticallyChecksObservation: NSKeyValueObservation?
|
||||||
private var lastCheckDateObservation: NSKeyValueObservation?
|
private var lastCheckDateObservation: NSKeyValueObservation?
|
||||||
|
#endif
|
||||||
|
|
||||||
@Published var automaticallyChecksForUpdates = false
|
@Published var automaticallyChecksForUpdates = false
|
||||||
@Published var lastUpdateCheckDate: Date?
|
@Published var lastUpdateCheckDate: Date?
|
||||||
|
|
||||||
private override init() {
|
private override init() {
|
||||||
super.init()
|
super.init()
|
||||||
|
#if !APPSTORE
|
||||||
setupUpdater()
|
setupUpdater()
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !APPSTORE
|
||||||
private func setupUpdater() {
|
private func setupUpdater() {
|
||||||
updaterController = SPUStandardUpdaterController(
|
updaterController = SPUStandardUpdaterController(
|
||||||
startingUpdater: true,
|
startingUpdater: true,
|
||||||
@@ -57,17 +64,24 @@ class UpdateManager: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
func checkForUpdates() {
|
func checkForUpdates() {
|
||||||
|
#if !APPSTORE
|
||||||
guard let updater = updaterController?.updater else {
|
guard let updater = updaterController?.updater else {
|
||||||
print("Updater not initialized")
|
print("Updater not initialized")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
updater.checkForUpdates()
|
updater.checkForUpdates()
|
||||||
|
#else
|
||||||
|
print("Updates are managed by the App Store")
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
#if !APPSTORE
|
||||||
automaticallyChecksObservation?.invalidate()
|
automaticallyChecksObservation?.invalidate()
|
||||||
lastCheckDateObservation?.invalidate()
|
lastCheckDateObservation?.invalidate()
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,13 +101,15 @@ struct LookAwayReminderView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func startCountdown() {
|
private func startCountdown() {
|
||||||
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
|
let timer = Timer(timeInterval: 1.0, repeats: true) { [self] _ in
|
||||||
if remainingSeconds > 0 {
|
if remainingSeconds > 0 {
|
||||||
remainingSeconds -= 1
|
remainingSeconds -= 1
|
||||||
} else {
|
} else {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RunLoop.current.add(timer, forMode: .common)
|
||||||
|
self.timer = timer
|
||||||
}
|
}
|
||||||
|
|
||||||
private func dismiss() {
|
private func dismiss() {
|
||||||
|
|||||||
@@ -101,13 +101,15 @@ struct UserTimerOverlayReminderView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func startCountdown() {
|
private func startCountdown() {
|
||||||
countdownTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
|
let timer = Timer(timeInterval: 1.0, repeats: true) { [self] _ in
|
||||||
if remainingSeconds > 0 {
|
if remainingSeconds > 0 {
|
||||||
remainingSeconds -= 1
|
remainingSeconds -= 1
|
||||||
} else {
|
} else {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RunLoop.current.add(timer, forMode: .common)
|
||||||
|
countdownTimer = timer
|
||||||
}
|
}
|
||||||
|
|
||||||
private func dismiss() {
|
private func dismiss() {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# Ensure we're using self-distribution configuration
|
||||||
|
echo "🔄 Switching to self-distribution configuration..."
|
||||||
|
./switch_to self
|
||||||
|
|
||||||
# Load environment variables from .env file
|
# Load environment variables from .env file
|
||||||
if [ -f .env ]; then
|
if [ -f .env ]; then
|
||||||
set -a
|
set -a
|
||||||
|
|||||||
348
switch_to
Executable file
348
switch_to
Executable file
@@ -0,0 +1,348 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Configuration paths
|
||||||
|
INFO_PLIST="Gaze/Info.plist"
|
||||||
|
ENTITLEMENTS="Gaze/Gaze.entitlements"
|
||||||
|
PROJECT_FILE="Gaze.xcodeproj/project.pbxproj"
|
||||||
|
BACKUP_DIR=".distribution_configs"
|
||||||
|
|
||||||
|
# Distribution configurations
|
||||||
|
APPSTORE_CONFIG="${BACKUP_DIR}/appstore"
|
||||||
|
SELF_CONFIG="${BACKUP_DIR}/self"
|
||||||
|
|
||||||
|
# Function to print colored messages
|
||||||
|
print_info() {
|
||||||
|
echo -e "${BLUE}ℹ${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}✓${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}⚠${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}✗${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to create backup directories
|
||||||
|
create_backup_dirs() {
|
||||||
|
mkdir -p "${APPSTORE_CONFIG}"
|
||||||
|
mkdir -p "${SELF_CONFIG}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to backup current configuration
|
||||||
|
backup_current_config() {
|
||||||
|
local config_name=$1
|
||||||
|
local config_dir=$2
|
||||||
|
|
||||||
|
print_info "Backing up ${config_name} configuration..."
|
||||||
|
|
||||||
|
# Backup Info.plist
|
||||||
|
if [ -f "${INFO_PLIST}" ]; then
|
||||||
|
cp "${INFO_PLIST}" "${config_dir}/Info.plist"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup entitlements
|
||||||
|
if [ -f "${ENTITLEMENTS}" ]; then
|
||||||
|
cp "${ENTITLEMENTS}" "${config_dir}/Gaze.entitlements"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup relevant parts of project.pbxproj (just the Release config)
|
||||||
|
if [ -f "${PROJECT_FILE}" ]; then
|
||||||
|
# Extract the Release configuration section
|
||||||
|
awk '/27A21B5F2F0F69DD0018C4F3 \/\* Release \*\/ = {/,/name = Release;/ {print}' "${PROJECT_FILE}" > "${config_dir}/project_release.txt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "Backed up ${config_name} configuration"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to restore configuration
|
||||||
|
restore_config() {
|
||||||
|
local config_name=$1
|
||||||
|
local config_dir=$2
|
||||||
|
|
||||||
|
print_info "Restoring ${config_name} configuration..."
|
||||||
|
|
||||||
|
# Restore Info.plist
|
||||||
|
if [ -f "${config_dir}/Info.plist" ]; then
|
||||||
|
cp "${config_dir}/Info.plist" "${INFO_PLIST}"
|
||||||
|
print_success "Restored Info.plist"
|
||||||
|
else
|
||||||
|
print_warning "No Info.plist backup found for ${config_name}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restore entitlements
|
||||||
|
if [ -f "${config_dir}/Gaze.entitlements" ]; then
|
||||||
|
cp "${config_dir}/Gaze.entitlements" "${ENTITLEMENTS}"
|
||||||
|
print_success "Restored entitlements"
|
||||||
|
else
|
||||||
|
print_warning "No entitlements backup found for ${config_name}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restore project.pbxproj Release configuration
|
||||||
|
if [ -f "${config_dir}/project_release.txt" ]; then
|
||||||
|
# Check if we're restoring to appstore or self mode based on file content
|
||||||
|
if grep -q "OTHER_SWIFT_FLAGS.*APPSTORE" "${config_dir}/project_release.txt"; then
|
||||||
|
# Add APPSTORE flag
|
||||||
|
if ! grep -q "OTHER_SWIFT_FLAGS.*APPSTORE" "${PROJECT_FILE}"; then
|
||||||
|
# Find the Release config section and add the flag
|
||||||
|
sed -i.backup '/27A21B5F2F0F69DD0018C4F3 \/\* Release \*\/ = {/,/name = Release;/{
|
||||||
|
/MARKETING_VERSION = /a\
|
||||||
|
OTHER_SWIFT_FLAGS = "-D APPSTORE";
|
||||||
|
}' "${PROJECT_FILE}"
|
||||||
|
rm -f "${PROJECT_FILE}.backup"
|
||||||
|
print_success "Added APPSTORE compiler flag"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Remove APPSTORE flag
|
||||||
|
if grep -q "OTHER_SWIFT_FLAGS.*APPSTORE" "${PROJECT_FILE}"; then
|
||||||
|
sed -i.backup '/OTHER_SWIFT_FLAGS = "-D APPSTORE";/d' "${PROJECT_FILE}"
|
||||||
|
rm -f "${PROJECT_FILE}.backup"
|
||||||
|
print_success "Removed APPSTORE compiler flag"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_warning "No project.pbxproj backup found for ${config_name}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to initialize configurations if they don't exist
|
||||||
|
initialize_configs() {
|
||||||
|
create_backup_dirs
|
||||||
|
|
||||||
|
# Check if we have existing backups
|
||||||
|
if [ ! -f "${SELF_CONFIG}/Info.plist" ]; then
|
||||||
|
print_info "No self-distribution config found. Creating from current state..."
|
||||||
|
|
||||||
|
# The current state should be self-distribution (before my changes)
|
||||||
|
# Let's create the self-distribution version with Sparkle keys
|
||||||
|
cat > "${SELF_CONFIG}/Info.plist" <<'EOF'
|
||||||
|
<?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>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>LSUIElement</key>
|
||||||
|
<true/>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.productivity</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>Gaze</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Gaze</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright © 2026 Mike Freno. All rights reserved.</string>
|
||||||
|
<key>SUPublicEDKey</key>
|
||||||
|
<string>Z2RmohI1y2bgeGQQUDqO9F0HNF2AzFotOt8CwGB6VJM=</string>
|
||||||
|
<key>SUFeedURL</key>
|
||||||
|
<string>https://freno.me/api/Gaze/appcast.xml</string>
|
||||||
|
<key>SUEnableAutomaticChecks</key>
|
||||||
|
<true/>
|
||||||
|
<key>SUScheduledCheckInterval</key>
|
||||||
|
<integer>86400</integer>
|
||||||
|
<key>SUEnableInstallerLauncherService</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "${SELF_CONFIG}/Gaze.entitlements" <<'EOF'
|
||||||
|
<?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>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
|
||||||
|
<array>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spks</string>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spki</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Extract current Release config WITHOUT APPSTORE flag
|
||||||
|
awk '/27A21B5F2F0F69DD0018C4F3 \/\* Release \*\/ = {/,/name = Release;/ {
|
||||||
|
if ($0 !~ /OTHER_SWIFT_FLAGS/) {
|
||||||
|
print
|
||||||
|
} else {
|
||||||
|
# Skip the APPSTORE flag line
|
||||||
|
if ($0 !~ /APPSTORE/) {
|
||||||
|
print
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' "${PROJECT_FILE}" > "${SELF_CONFIG}/project_release.txt"
|
||||||
|
|
||||||
|
print_success "Created self-distribution config"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${APPSTORE_CONFIG}/Info.plist" ]; then
|
||||||
|
print_info "Creating App Store config..."
|
||||||
|
|
||||||
|
# Backup current state as App Store config (after my changes)
|
||||||
|
backup_current_config "App Store" "${APPSTORE_CONFIG}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to show current distribution mode
|
||||||
|
show_current_mode() {
|
||||||
|
print_info "Current configuration:"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check for Sparkle keys in Info.plist
|
||||||
|
if grep -q "SUPublicEDKey" "${INFO_PLIST}" 2>/dev/null; then
|
||||||
|
echo " Info.plist: ${GREEN}Self-Distribution${NC} (has Sparkle keys)"
|
||||||
|
else
|
||||||
|
echo " Info.plist: ${BLUE}App Store${NC} (no Sparkle keys)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for Sparkle entitlements
|
||||||
|
if grep -q "mach-lookup.global-name" "${ENTITLEMENTS}" 2>/dev/null; then
|
||||||
|
echo " Entitlements: ${GREEN}Self-Distribution${NC} (has Sparkle exceptions)"
|
||||||
|
else
|
||||||
|
echo " Entitlements: ${BLUE}App Store${NC} (no Sparkle exceptions)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for APPSTORE flag
|
||||||
|
if grep -q "OTHER_SWIFT_FLAGS.*APPSTORE" "${PROJECT_FILE}" 2>/dev/null; then
|
||||||
|
echo " Build Settings: ${BLUE}App Store${NC} (has APPSTORE flag)"
|
||||||
|
else
|
||||||
|
echo " Build Settings: ${GREEN}Self-Distribution${NC} (no APPSTORE flag)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to switch to App Store configuration
|
||||||
|
switch_to_appstore() {
|
||||||
|
print_info "Switching to App Store distribution configuration..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Backup current state if it's self-distribution
|
||||||
|
if grep -q "SUPublicEDKey" "${INFO_PLIST}" 2>/dev/null; then
|
||||||
|
backup_current_config "self-distribution" "${SELF_CONFIG}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restore App Store config
|
||||||
|
restore_config "App Store" "${APPSTORE_CONFIG}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_success "Switched to App Store distribution mode"
|
||||||
|
print_warning "Remember to add the Run Script phase to remove Sparkle framework!"
|
||||||
|
echo ""
|
||||||
|
echo "Run Script to add in Xcode Build Phases:"
|
||||||
|
echo '#!/bin/bash'
|
||||||
|
echo 'if [[ "${OTHER_SWIFT_FLAGS}" == *"APPSTORE"* ]]; then'
|
||||||
|
echo ' rm -rf "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sparkle.framework"'
|
||||||
|
echo 'fi'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to switch to self-distribution configuration
|
||||||
|
switch_to_self() {
|
||||||
|
print_info "Switching to self-distribution configuration..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Backup current state if it's App Store
|
||||||
|
if ! grep -q "SUPublicEDKey" "${INFO_PLIST}" 2>/dev/null; then
|
||||||
|
backup_current_config "App Store" "${APPSTORE_CONFIG}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restore self-distribution config
|
||||||
|
restore_config "self-distribution" "${SELF_CONFIG}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_success "Switched to self-distribution mode"
|
||||||
|
print_info "Sparkle auto-updates enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to show usage
|
||||||
|
show_usage() {
|
||||||
|
cat << EOF
|
||||||
|
${BLUE}Gaze Distribution Configuration Switcher${NC}
|
||||||
|
|
||||||
|
${GREEN}Usage:${NC}
|
||||||
|
./switch_to [command]
|
||||||
|
|
||||||
|
${GREEN}Commands:${NC}
|
||||||
|
appstore Switch to App Store distribution configuration
|
||||||
|
- Removes Sparkle keys from Info.plist
|
||||||
|
- Removes Sparkle entitlements
|
||||||
|
- Adds APPSTORE compiler flag
|
||||||
|
|
||||||
|
self Switch to self-distribution configuration
|
||||||
|
- Adds Sparkle keys to Info.plist
|
||||||
|
- Adds Sparkle entitlements for XPC services
|
||||||
|
- Removes APPSTORE compiler flag
|
||||||
|
|
||||||
|
status Show current distribution configuration
|
||||||
|
|
||||||
|
help Show this help message
|
||||||
|
|
||||||
|
${GREEN}Examples:${NC}
|
||||||
|
./switch_to appstore # Prepare for App Store submission
|
||||||
|
./switch_to self # Prepare for direct distribution with auto-updates
|
||||||
|
./switch_to status # Check current configuration
|
||||||
|
|
||||||
|
${YELLOW}Note:${NC} Configuration backups are stored in ${BACKUP_DIR}/
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script logic
|
||||||
|
main() {
|
||||||
|
# Check if we're in the right directory
|
||||||
|
if [ ! -f "${PROJECT_FILE}" ]; then
|
||||||
|
print_error "Not in Gaze project directory. Please run from project root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Initialize configurations if needed
|
||||||
|
initialize_configs
|
||||||
|
|
||||||
|
# Parse command
|
||||||
|
case "${1:-}" in
|
||||||
|
appstore)
|
||||||
|
switch_to_appstore
|
||||||
|
;;
|
||||||
|
self)
|
||||||
|
switch_to_self
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
show_current_mode
|
||||||
|
;;
|
||||||
|
help|--help|-h)
|
||||||
|
show_usage
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_error "Unknown command: ${1:-}"
|
||||||
|
echo ""
|
||||||
|
show_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user