fix: manage sparkle in switch_to
This commit is contained in:
@@ -1,201 +0,0 @@
|
|||||||
# Archive Post-Action Setup (REQUIRED for App Store)
|
|
||||||
|
|
||||||
## The Problem
|
|
||||||
|
|
||||||
App Store validation fails with:
|
|
||||||
```
|
|
||||||
App sandbox not enabled. The following executables must include the
|
|
||||||
"com.apple.security.app-sandbox" entitlement...
|
|
||||||
Sparkle.framework/Versions/B/Autoupdate
|
|
||||||
Sparkle.framework/Versions/B/Updater.app
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
Sparkle.framework **must be physically removed** from the app bundle for App Store distribution.
|
|
||||||
|
|
||||||
## Why Build Phase Scripts Don't Work
|
|
||||||
|
|
||||||
Build Phase scripts run during compilation when files are code-signed and locked by macOS. Even with `chmod` and `chflags`, you get "Operation not permitted" due to System Integrity Protection.
|
|
||||||
|
|
||||||
## The Solution: Archive Post-Action
|
|
||||||
|
|
||||||
Archive Post-Actions run **after** the archive completes, when files are no longer locked. This is the correct place to remove Sparkle.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Setup Instructions (2 minutes)
|
|
||||||
|
|
||||||
### 1. Open Scheme Editor
|
|
||||||
In Xcode: **Product → Scheme → Edit Scheme...** (or press **⌘<**)
|
|
||||||
|
|
||||||
### 2. Select Archive
|
|
||||||
Click **Archive** in the left sidebar
|
|
||||||
|
|
||||||
### 3. Add Post-Action Script
|
|
||||||
- At the bottom, under "Post-actions", click the **+** button
|
|
||||||
- Select **New Run Script Action**
|
|
||||||
|
|
||||||
### 4. Configure the Action
|
|
||||||
|
|
||||||
**Provide build settings from:** Select **Gaze** from the dropdown (IMPORTANT!)
|
|
||||||
|
|
||||||
**Shell:** Leave as `/bin/bash`
|
|
||||||
|
|
||||||
**Script:** Copy and paste this entire script:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
if [[ "${OTHER_SWIFT_FLAGS}" == *"APPSTORE"* ]]; then
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
echo "🗑️ Removing Sparkle from archived app..."
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
|
|
||||||
SPARKLE_PATH="${ARCHIVE_PATH}/Products/Applications/Gaze.app/Contents/Frameworks/Sparkle.framework"
|
|
||||||
|
|
||||||
if [ -d "$SPARKLE_PATH" ]; then
|
|
||||||
echo "📂 Found Sparkle at: $SPARKLE_PATH"
|
|
||||||
|
|
||||||
# Make writable and remove
|
|
||||||
chmod -R u+w "$SPARKLE_PATH" 2>/dev/null || true
|
|
||||||
chflags -R nouchg "$SPARKLE_PATH" 2>/dev/null || true
|
|
||||||
rm -rf "$SPARKLE_PATH"
|
|
||||||
|
|
||||||
if [ ! -d "$SPARKLE_PATH" ]; then
|
|
||||||
echo "✅ Sparkle framework removed successfully!"
|
|
||||||
else
|
|
||||||
echo "❌ ERROR: Could not remove Sparkle framework"
|
|
||||||
echo " This will cause App Store validation to fail"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "ℹ️ Sparkle framework not found (already removed)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
echo "✅ Archive ready for App Store distribution"
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
||||||
else
|
|
||||||
echo "✓ Self-distribution archive - Sparkle retained"
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Save
|
|
||||||
Click **Close** to save the scheme changes
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Verification Steps
|
|
||||||
|
|
||||||
### Test the Archive
|
|
||||||
|
|
||||||
1. **Switch to App Store mode:**
|
|
||||||
```bash
|
|
||||||
./switch_to appstore
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Archive in Xcode:**
|
|
||||||
- **Product → Archive** (or **⌘⇧B** then Archive)
|
|
||||||
- Watch the build log - you should see the post-action output
|
|
||||||
|
|
||||||
3. **Check the archive contents:**
|
|
||||||
- **Window → Organizer**
|
|
||||||
- Select your latest Gaze archive
|
|
||||||
- Right-click → **Show in Finder**
|
|
||||||
- Right-click the `.xcarchive` file → **Show Package Contents**
|
|
||||||
- Navigate to: `Products/Applications/Gaze.app/Contents/Frameworks/`
|
|
||||||
- **Verify:** Only `Lottie.framework` should be present ✅
|
|
||||||
|
|
||||||
4. **Distribute to App Store:**
|
|
||||||
- In Organizer, click **Distribute App**
|
|
||||||
- Choose **App Store Connect**
|
|
||||||
- Complete the distribution wizard
|
|
||||||
- **Validation should now pass!** ✅
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What You Should See
|
|
||||||
|
|
||||||
### In the Build Log (after archiving):
|
|
||||||
```
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
🗑️ Removing Sparkle from archived app...
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
📂 Found Sparkle at: [path]
|
|
||||||
✅ Sparkle framework removed successfully!
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
✅ Archive ready for App Store distribution
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
```
|
|
||||||
|
|
||||||
### In the Frameworks folder:
|
|
||||||
```
|
|
||||||
Gaze.app/Contents/Frameworks/
|
|
||||||
└── Lottie.framework/
|
|
||||||
```
|
|
||||||
|
|
||||||
No Sparkle.framework! ✅
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### "I don't see the post-action output"
|
|
||||||
- Make sure you selected **Gaze** in "Provide build settings from"
|
|
||||||
- Check View → Navigators → Show Report Navigator (⌘9)
|
|
||||||
- Select the Archive action to see full logs
|
|
||||||
|
|
||||||
### "Sparkle is still in the archive"
|
|
||||||
- Verify `./switch_to status` shows "App Store" for all items
|
|
||||||
- Check the script exactly matches (copy/paste the entire script)
|
|
||||||
- Try cleaning: Product → Clean Build Folder (⌘⇧K)
|
|
||||||
|
|
||||||
### "Script says 'Sparkle framework not found'"
|
|
||||||
- This means Sparkle wasn't embedded (good!)
|
|
||||||
- Continue with distribution - validation should pass
|
|
||||||
|
|
||||||
### "Archive Post-Action section doesn't exist"
|
|
||||||
- Make sure you're editing the **Archive** section, not Run or Test
|
|
||||||
- Click the triangle next to "Archive" to expand it
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Optional: Remove Old Build Phase Script
|
|
||||||
|
|
||||||
If you previously added a Build Phase script (which doesn't work due to file locking), you can remove it:
|
|
||||||
|
|
||||||
1. Gaze target → Build Phases
|
|
||||||
2. Find "Remove Sparkle for App Store" or "Run Script"
|
|
||||||
3. Click the **X** to delete it
|
|
||||||
|
|
||||||
The Archive Post-Action is the correct and only solution needed.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Why This Is Required
|
|
||||||
|
|
||||||
Even though:
|
|
||||||
- ✅ Sparkle code is disabled via `#if !APPSTORE`
|
|
||||||
- ✅ Info.plist has no Sparkle keys
|
|
||||||
- ✅ Entitlements have no Sparkle exceptions
|
|
||||||
|
|
||||||
App Store validation **still checks** for the physical presence of unsandboxed executables in frameworks. Sparkle contains XPC services that aren't App Store compatible, so the entire framework must be removed.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## For Self-Distribution
|
|
||||||
|
|
||||||
When building for self-distribution (`./switch_to self`), the script detects the absence of the APPSTORE flag and leaves Sparkle intact. You don't need to change anything!
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./switch_to self
|
|
||||||
./self_distribute # Sparkle is retained and works normally
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
✅ **One-time setup:** Add Archive Post-Action script
|
|
||||||
✅ **Works automatically:** Removes Sparkle only for App Store builds
|
|
||||||
✅ **Zero maintenance:** Once configured, runs automatically forever
|
|
||||||
|
|
||||||
This is the **correct and only working solution** for removing Sparkle from App Store builds!
|
|
||||||
296
DEPLOYMENT.md
296
DEPLOYMENT.md
@@ -1,296 +0,0 @@
|
|||||||
# Gaze Release Deployment Process
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This document describes the complete process for building and deploying new releases of Gaze with Sparkle auto-update support.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Xcode with Gaze project configured
|
|
||||||
- `create-dmg` tool installed (`brew install create-dmg`)
|
|
||||||
- Sparkle EdDSA signing keys in macOS Keychain (see Key Management below)
|
|
||||||
- AWS credentials configured in `.env` file for S3 upload
|
|
||||||
- Access to freno.me hosting infrastructure
|
|
||||||
|
|
||||||
## Version Management
|
|
||||||
|
|
||||||
Version numbers are managed in Xcode project settings:
|
|
||||||
- **Marketing Version** (`MARKETING_VERSION`): User-facing version (e.g., "0.1.1")
|
|
||||||
- **Build Number** (`CURRENT_PROJECT_VERSION`): Internal build number (e.g., "1")
|
|
||||||
|
|
||||||
These must be incremented before each release and kept in sync with `build_dmg` script.
|
|
||||||
|
|
||||||
## Release Checklist
|
|
||||||
|
|
||||||
### 1. Prepare Release
|
|
||||||
|
|
||||||
- [ ] Update version numbers in Xcode:
|
|
||||||
- Project Settings → General → Identity
|
|
||||||
- Set Marketing Version (e.g., "0.1.2")
|
|
||||||
- Increment Build Number (e.g., "2")
|
|
||||||
- [ ] Update `VERSION` and `BUILD_NUMBER` in `build_dmg` script
|
|
||||||
- [ ] Update CHANGELOG or release notes
|
|
||||||
- [ ] Commit version changes: `git commit -am "Bump version to X.Y.Z"`
|
|
||||||
- [ ] Create git tag: `git tag v0.1.2`
|
|
||||||
|
|
||||||
### 2. Build Application
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build the app in Xcode (Product → Archive → Export)
|
|
||||||
# Or use the run script
|
|
||||||
./run build
|
|
||||||
|
|
||||||
# Verify the app runs correctly
|
|
||||||
open Gaze.app
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Create DMG and Appcast
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run the build_dmg script
|
|
||||||
./build_dmg
|
|
||||||
|
|
||||||
# This will:
|
|
||||||
# - Create versioned DMG file
|
|
||||||
# - Generate appcast.xml with EdDSA signature
|
|
||||||
# - Upload to S3 if AWS credentials are configured
|
|
||||||
# - Display next steps
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Verify Artifacts
|
|
||||||
|
|
||||||
Check that the following files were created in `./releases/`:
|
|
||||||
- `Gaze-X.Y.Z.dmg` - Installable disk image
|
|
||||||
- `appcast.xml` - Update feed with signature
|
|
||||||
- `Gaze-X.Y.Z.delta` (optional) - Delta update from previous version
|
|
||||||
|
|
||||||
### 5. Upload to Hosting (if not using S3 auto-upload)
|
|
||||||
|
|
||||||
**DMG File:**
|
|
||||||
```bash
|
|
||||||
# Upload to: https://freno.me/downloads/
|
|
||||||
scp ./releases/Gaze-X.Y.Z.dmg your-server:/path/to/downloads/
|
|
||||||
```
|
|
||||||
|
|
||||||
**Appcast File:**
|
|
||||||
```bash
|
|
||||||
# Upload to: https://freno.me/api/Gaze/
|
|
||||||
scp ./releases/appcast.xml your-server:/path/to/api/Gaze/
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Verify Deployment
|
|
||||||
|
|
||||||
Test that files are accessible via HTTPS:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Test appcast accessibility
|
|
||||||
curl -I https://freno.me/api/Gaze/appcast.xml
|
|
||||||
# Should return: HTTP/2 200, content-type: application/xml
|
|
||||||
|
|
||||||
# Test DMG accessibility
|
|
||||||
curl -I https://freno.me/downloads/Gaze-X.Y.Z.dmg
|
|
||||||
# Should return: HTTP/2 200, content-type: application/octet-stream
|
|
||||||
|
|
||||||
# Validate appcast XML structure
|
|
||||||
curl https://freno.me/api/Gaze/appcast.xml | xmllint --format -
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7. Test Update Flow
|
|
||||||
|
|
||||||
**Manual Testing:**
|
|
||||||
1. Install previous version of Gaze
|
|
||||||
2. Launch app and check Settings → General → Software Updates
|
|
||||||
3. Click "Check for Updates Now"
|
|
||||||
4. Verify update notification appears
|
|
||||||
5. Complete update installation
|
|
||||||
6. Verify new version launches correctly
|
|
||||||
|
|
||||||
**Automatic Update Testing:**
|
|
||||||
1. Set `SUScheduledCheckInterval` to a low value (e.g., 60 seconds) for testing
|
|
||||||
2. Install previous version
|
|
||||||
3. Wait for automatic check
|
|
||||||
4. Verify update notification appears
|
|
||||||
|
|
||||||
### 8. Finalize Release
|
|
||||||
|
|
||||||
- [ ] Push git tag: `git push origin v0.1.2`
|
|
||||||
- [ ] Create GitHub release (optional)
|
|
||||||
- [ ] Announce release to users
|
|
||||||
- [ ] Monitor for update errors in the first 24 hours
|
|
||||||
|
|
||||||
## Hosting Configuration
|
|
||||||
|
|
||||||
### Current Setup
|
|
||||||
|
|
||||||
- **Appcast URL:** `https://freno.me/api/Gaze/appcast.xml`
|
|
||||||
- **Download URL:** `https://freno.me/downloads/Gaze-{VERSION}.dmg`
|
|
||||||
- **Hosting:** AWS S3 with freno.me domain
|
|
||||||
- **SSL:** HTTPS enabled (required by App Transport Security)
|
|
||||||
|
|
||||||
### AWS S3 Configuration
|
|
||||||
|
|
||||||
Create a `.env` file in the project root with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
AWS_ACCESS_KEY_ID=your_access_key
|
|
||||||
AWS_SECRET_ACCESS_KEY=your_secret_key
|
|
||||||
AWS_BUCKET_NAME=your_bucket_name
|
|
||||||
AWS_REGION=us-east-1
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** `.env` is gitignored to protect credentials.
|
|
||||||
|
|
||||||
### S3 Bucket Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
your-bucket/
|
|
||||||
├── downloads/
|
|
||||||
│ ├── Gaze-0.1.1.dmg
|
|
||||||
│ ├── Gaze-0.1.2.dmg
|
|
||||||
│ └── ...
|
|
||||||
└── api/
|
|
||||||
└── Gaze/
|
|
||||||
└── appcast.xml
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Management
|
|
||||||
|
|
||||||
### EdDSA Signing Keys
|
|
||||||
|
|
||||||
**Location:**
|
|
||||||
- **Public Key:** In `Gaze/Info.plist` as `SUPublicEDKey`
|
|
||||||
- **Private Key:** In macOS Keychain as "Sparkle EdDSA Private Key"
|
|
||||||
- **Backup:** `~/sparkle_private_key_backup.pem` (keep secure!)
|
|
||||||
|
|
||||||
**Current Public Key:**
|
|
||||||
```
|
|
||||||
Z2RmohI1y2bgeGQQUDqO9F0HNF2AzFotOt8CwGB6VJM=
|
|
||||||
```
|
|
||||||
|
|
||||||
**Security:**
|
|
||||||
- Never commit private key to version control
|
|
||||||
- Keep backup in secure location (password manager, encrypted drive)
|
|
||||||
- Private key is required to sign all future updates
|
|
||||||
|
|
||||||
### Regenerating Keys (Emergency Only)
|
|
||||||
|
|
||||||
If private key is lost, you must:
|
|
||||||
1. Generate new key pair: `./generate_keys`
|
|
||||||
2. Update `SUPublicEDKey` in Info.plist
|
|
||||||
3. Release new version with new public key
|
|
||||||
4. Previous versions won't be able to update (users must manually install)
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Appcast Generation Fails
|
|
||||||
|
|
||||||
**Problem:** `generate_appcast` tool not found
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
```bash
|
|
||||||
# Build the app first to generate Sparkle tools
|
|
||||||
xcodebuild -project Gaze.xcodeproj -scheme Gaze -configuration Release
|
|
||||||
|
|
||||||
# Find Sparkle tools
|
|
||||||
find ~/Library/Developer/Xcode/DerivedData/Gaze-* -name "generate_appcast"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Update Check Fails in App
|
|
||||||
|
|
||||||
**Problem:** No updates found or connection error
|
|
||||||
|
|
||||||
**Diagnostics:**
|
|
||||||
```bash
|
|
||||||
# Check Console.app for Sparkle logs
|
|
||||||
# Filter by process: Gaze
|
|
||||||
# Look for:
|
|
||||||
# - "Downloading appcast..."
|
|
||||||
# - "Appcast downloaded successfully"
|
|
||||||
# - Connection errors
|
|
||||||
# - Signature verification errors
|
|
||||||
```
|
|
||||||
|
|
||||||
**Common Issues:**
|
|
||||||
- Appcast URL not accessible (check HTTPS)
|
|
||||||
- Signature mismatch (wrong private key used)
|
|
||||||
- XML malformed (validate with xmllint)
|
|
||||||
- Version number not higher than current version
|
|
||||||
|
|
||||||
### DMG Not Downloading
|
|
||||||
|
|
||||||
**Problem:** Update found but download fails
|
|
||||||
|
|
||||||
**Check:**
|
|
||||||
- DMG URL is correct in appcast
|
|
||||||
- DMG file is accessible via HTTPS
|
|
||||||
- File size in appcast matches actual DMG size
|
|
||||||
- No CORS issues (check browser console)
|
|
||||||
|
|
||||||
## Delta Updates
|
|
||||||
|
|
||||||
Sparkle automatically generates delta updates when multiple versions exist in `./releases/`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Keep previous versions for delta generation
|
|
||||||
releases/
|
|
||||||
├── Gaze-0.1.1.dmg
|
|
||||||
├── Gaze-0.1.2.dmg
|
|
||||||
├── Gaze-0.1.1-to-0.1.2.delta # Generated automatically
|
|
||||||
└── appcast.xml
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits:**
|
|
||||||
- Much smaller downloads (MB vs GB)
|
|
||||||
- Faster updates for users
|
|
||||||
- Generated automatically by `generate_appcast`
|
|
||||||
|
|
||||||
**Note:** First-time users still download full DMG.
|
|
||||||
|
|
||||||
## Testing with Local Appcast
|
|
||||||
|
|
||||||
For testing without deploying:
|
|
||||||
|
|
||||||
1. Modify Info.plist temporarily:
|
|
||||||
```xml
|
|
||||||
<key>SUFeedURL</key>
|
|
||||||
<string>file:///Users/mike/Code/Gaze/releases/appcast.xml</string>
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Build and run app
|
|
||||||
3. Check for updates
|
|
||||||
4. Revert Info.plist before committing
|
|
||||||
|
|
||||||
## Release Notes
|
|
||||||
|
|
||||||
Release notes are embedded in appcast XML as CDATA:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<description><![CDATA[
|
|
||||||
<h2>What's New in Version X.Y.Z</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Feature 1</li>
|
|
||||||
<li>Bug fix 2</li>
|
|
||||||
</ul>
|
|
||||||
]]></description>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Tips:**
|
|
||||||
- Use simple HTML (h2, ul, li, p, strong, em)
|
|
||||||
- No external resources (images, CSS, JS)
|
|
||||||
- Keep concise and user-focused
|
|
||||||
- Highlight breaking changes
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [Sparkle Documentation](https://sparkle-project.org/documentation/)
|
|
||||||
- [Publishing Updates](https://sparkle-project.org/documentation/publishing/)
|
|
||||||
- [Sandboxed Apps](https://sparkle-project.org/documentation/sandboxing/)
|
|
||||||
- [Gaze Repository](https://github.com/YOUR_USERNAME/Gaze)
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
For issues with deployment:
|
|
||||||
1. Check Console.app for Sparkle errors
|
|
||||||
2. Verify appcast validation with xmllint
|
|
||||||
3. Test with file:// URL first
|
|
||||||
4. Check AWS S3 permissions and CORS
|
|
||||||
214
DISTRIBUTION.md
214
DISTRIBUTION.md
@@ -1,214 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
275915892F132A9200D0E60D /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 27AE10B12F10B1FC00E00DBC /* Lottie */; };
|
275915892F132A9200D0E60D /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 27AE10B12F10B1FC00E00DBC /* Lottie */; };
|
||||||
275915902F132B0000D0E60D /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 27AE10B22F10B20000E00DBC /* Sparkle */; settings = {ATTRIBUTES = (Weak, ); }; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -71,7 +70,6 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
275915892F132A9200D0E60D /* Lottie in Frameworks */,
|
275915892F132A9200D0E60D /* Lottie in Frameworks */,
|
||||||
275915902F132B0000D0E60D /* Sparkle in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -133,7 +131,6 @@
|
|||||||
name = Gaze;
|
name = Gaze;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
27AE10B12F10B1FC00E00DBC /* Lottie */,
|
27AE10B12F10B1FC00E00DBC /* Lottie */,
|
||||||
27AE10B22F10B20000E00DBC /* Sparkle */,
|
|
||||||
);
|
);
|
||||||
productName = Gaze;
|
productName = Gaze;
|
||||||
productReference = 27A21B3C2F0F69DC0018C4F3 /* Gaze.app */;
|
productReference = 27A21B3C2F0F69DC0018C4F3 /* Gaze.app */;
|
||||||
@@ -219,7 +216,6 @@
|
|||||||
minimizedProjectReferenceProxies = 1;
|
minimizedProjectReferenceProxies = 1;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
27AE10B02F10B1FC00E00DBC /* XCRemoteSwiftPackageReference "lottie-spm" */,
|
27AE10B02F10B1FC00E00DBC /* XCRemoteSwiftPackageReference "lottie-spm" */,
|
||||||
27AE10B32F10B21000E00DBC /* XCRemoteSwiftPackageReference "Sparkle" */,
|
|
||||||
);
|
);
|
||||||
preferredProjectObjectVersion = 77;
|
preferredProjectObjectVersion = 77;
|
||||||
productRefGroup = 27A21B3D2F0F69DC0018C4F3 /* Products */;
|
productRefGroup = 27A21B3D2F0F69DC0018C4F3 /* Products */;
|
||||||
@@ -424,7 +420,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 6;
|
CURRENT_PROJECT_VERSION = 8;
|
||||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -460,7 +456,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 6;
|
CURRENT_PROJECT_VERSION = 8;
|
||||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -618,14 +614,6 @@
|
|||||||
minimumVersion = 4.6.0;
|
minimumVersion = 4.6.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
27AE10B32F10B21000E00DBC /* XCRemoteSwiftPackageReference "Sparkle" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/sparkle-project/Sparkle";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMajorVersion;
|
|
||||||
minimumVersion = 2.0.0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
@@ -634,11 +622,6 @@
|
|||||||
package = 27AE10B02F10B1FC00E00DBC /* XCRemoteSwiftPackageReference "lottie-spm" */;
|
package = 27AE10B02F10B1FC00E00DBC /* XCRemoteSwiftPackageReference "lottie-spm" */;
|
||||||
productName = Lottie;
|
productName = Lottie;
|
||||||
};
|
};
|
||||||
27AE10B22F10B20000E00DBC /* Sparkle */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = 27AE10B32F10B21000E00DBC /* XCRemoteSwiftPackageReference "Sparkle" */;
|
|
||||||
productName = Sparkle;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 27A21B342F0F69DC0018C4F3 /* Project object */;
|
rootObject = 27A21B342F0F69DC0018C4F3 /* Project object */;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "513d974fbede884a919977d3446360023f6e3239ac314f4fbd9657e80aca7560",
|
"originHash" : "6b3386dc9ff1f3a74f1534de9c41d47137eae0901cfe819ed442f1b241549359",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "lottie-spm",
|
"identity" : "lottie-spm",
|
||||||
@@ -9,15 +9,6 @@
|
|||||||
"revision" : "69faaefa7721fba9e434a52c16adf4329c9084db",
|
"revision" : "69faaefa7721fba9e434a52c16adf4329c9084db",
|
||||||
"version" : "4.6.0"
|
"version" : "4.6.0"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "sparkle",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/sparkle-project/Sparkle",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "5581748cef2bae787496fe6d61139aebe0a451f6",
|
|
||||||
"version" : "2.8.1"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version" : 3
|
"version" : 3
|
||||||
|
|||||||
@@ -98,23 +98,5 @@
|
|||||||
<ArchiveAction
|
<ArchiveAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
revealArchiveInOrganizer = "YES">
|
revealArchiveInOrganizer = "YES">
|
||||||
<PostActions>
|
|
||||||
<ExecutionAction
|
|
||||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
|
||||||
<ActionContent
|
|
||||||
title = "Run Script"
|
|
||||||
scriptText = "if [[ "${OTHER_SWIFT_FLAGS}" == *"APPSTORE"* ]]; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "🗑️ Removing Sparkle from archived app..." echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" SPARKLE_PATH="${ARCHIVE_PATH}/Products/Applications/Gaze.app/Contents/Frameworks/Sparkle.framework" if [ -d "$SPARKLE_PATH" ]; then echo "📂 Found Sparkle at: $SPARKLE_PATH" # Make writable and remove chmod -R u+w "$SPARKLE_PATH" 2>/dev/null || true chflags -R nouchg "$SPARKLE_PATH" 2>/dev/null || true rm -rf "$SPARKLE_PATH" if [ ! -d "$SPARKLE_PATH" ]; then echo "✅ Sparkle framework removed successfully!" else echo "❌ ERROR: Could not remove Sparkle framework" echo " This will cause App Store validation to fail" exit 1 fi else echo "ℹ️ Sparkle framework not found (already removed)" fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "✅ Archive ready for App Store distribution" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" else echo "✓ Self-distribution archive - Sparkle retained" fi ">
|
|
||||||
<EnvironmentBuildable>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "27A21B3B2F0F69DC0018C4F3"
|
|
||||||
BuildableName = "Gaze.app"
|
|
||||||
BlueprintName = "Gaze"
|
|
||||||
ReferencedContainer = "container:Gaze.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</EnvironmentBuildable>
|
|
||||||
</ActionContent>
|
|
||||||
</ExecutionAction>
|
|
||||||
</PostActions>
|
|
||||||
</ArchiveAction>
|
</ArchiveAction>
|
||||||
</Scheme>
|
</Scheme>
|
||||||
|
|||||||
144
switch_to
144
switch_to
@@ -12,12 +12,17 @@ NC='\033[0m' # No Color
|
|||||||
INFO_PLIST="Gaze/Info.plist"
|
INFO_PLIST="Gaze/Info.plist"
|
||||||
ENTITLEMENTS="Gaze/Gaze.entitlements"
|
ENTITLEMENTS="Gaze/Gaze.entitlements"
|
||||||
PROJECT_FILE="Gaze.xcodeproj/project.pbxproj"
|
PROJECT_FILE="Gaze.xcodeproj/project.pbxproj"
|
||||||
|
PACKAGE_RESOLVED="Gaze.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved"
|
||||||
BACKUP_DIR=".distribution_configs"
|
BACKUP_DIR=".distribution_configs"
|
||||||
|
|
||||||
# Distribution configurations
|
# Distribution configurations
|
||||||
APPSTORE_CONFIG="${BACKUP_DIR}/appstore"
|
APPSTORE_CONFIG="${BACKUP_DIR}/appstore"
|
||||||
SELF_CONFIG="${BACKUP_DIR}/self"
|
SELF_CONFIG="${BACKUP_DIR}/self"
|
||||||
|
|
||||||
|
# Sparkle package details
|
||||||
|
SPARKLE_REPO="https://github.com/sparkle-project/Sparkle"
|
||||||
|
SPARKLE_VERSION="2.8.1"
|
||||||
|
|
||||||
# Function to print colored messages
|
# Function to print colored messages
|
||||||
print_info() {
|
print_info() {
|
||||||
echo -e "${BLUE}ℹ${NC} $1"
|
echo -e "${BLUE}ℹ${NC} $1"
|
||||||
@@ -35,6 +40,113 @@ print_error() {
|
|||||||
echo -e "${RED}✗${NC} $1"
|
echo -e "${RED}✗${NC} $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Function to check if Sparkle is in Package.resolved
|
||||||
|
has_sparkle_package() {
|
||||||
|
if [ -f "${PACKAGE_RESOLVED}" ]; then
|
||||||
|
grep -q "Sparkle" "${PACKAGE_RESOLVED}"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to remove Sparkle from Xcode project
|
||||||
|
remove_sparkle_package() {
|
||||||
|
print_info "Removing Sparkle package dependency..."
|
||||||
|
|
||||||
|
# Check if Sparkle exists in the project
|
||||||
|
if ! has_sparkle_package && ! grep -q "Sparkle" "${PROJECT_FILE}"; then
|
||||||
|
print_info "Sparkle already removed"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup project file
|
||||||
|
cp "${PROJECT_FILE}" "${PROJECT_FILE}.backup"
|
||||||
|
|
||||||
|
# Remove Sparkle from Package.resolved
|
||||||
|
if [ -f "${PACKAGE_RESOLVED}" ]; then
|
||||||
|
# Create a temp file without Sparkle entry
|
||||||
|
python3 -c "
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open('${PACKAGE_RESOLVED}', 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
# Filter out Sparkle from pins
|
||||||
|
if 'pins' in data:
|
||||||
|
data['pins'] = [pin for pin in data['pins'] if 'sparkle' not in pin.get('identity', '').lower()]
|
||||||
|
|
||||||
|
with open('${PACKAGE_RESOLVED}', 'w') as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
|
||||||
|
print('✓ Updated Package.resolved')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'Error: {e}', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
print_error "Failed to update Package.resolved"
|
||||||
|
mv "${PROJECT_FILE}.backup" "${PROJECT_FILE}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove Sparkle references from project.pbxproj
|
||||||
|
# This removes: package references, product dependencies, and framework build files
|
||||||
|
|
||||||
|
# Remove XCRemoteSwiftPackageReference for Sparkle
|
||||||
|
sed -i '' '/XCRemoteSwiftPackageReference "Sparkle"/,/};/d' "${PROJECT_FILE}"
|
||||||
|
|
||||||
|
# Remove XCSwiftPackageProductDependency for Sparkle
|
||||||
|
sed -i '' '/XCSwiftPackageProductDependency "Sparkle"/,/};/d' "${PROJECT_FILE}"
|
||||||
|
|
||||||
|
# Remove Sparkle from packageProductDependencies array
|
||||||
|
sed -i '' '/\/\* Sparkle \*\/,/d' "${PROJECT_FILE}"
|
||||||
|
|
||||||
|
# Remove Sparkle from Frameworks build phase
|
||||||
|
sed -i '' '/Sparkle in Frameworks/d' "${PROJECT_FILE}"
|
||||||
|
|
||||||
|
# Clean up empty lines
|
||||||
|
sed -i '' '/^[[:space:]]*$/d' "${PROJECT_FILE}"
|
||||||
|
|
||||||
|
rm -f "${PROJECT_FILE}.backup"
|
||||||
|
|
||||||
|
print_success "Removed Sparkle package dependency"
|
||||||
|
print_info "Xcode will resolve packages on next build"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to add Sparkle to Xcode project
|
||||||
|
add_sparkle_package() {
|
||||||
|
print_info "Adding Sparkle package dependency..."
|
||||||
|
|
||||||
|
# Check if Sparkle already exists
|
||||||
|
if has_sparkle_package || grep -q "Sparkle" "${PROJECT_FILE}"; then
|
||||||
|
print_info "Sparkle already present"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_warning "Sparkle package must be added manually in Xcode:"
|
||||||
|
echo ""
|
||||||
|
echo " 1. Open Gaze.xcodeproj in Xcode"
|
||||||
|
echo " 2. Select the project in the navigator"
|
||||||
|
echo " 3. Go to 'Package Dependencies' tab"
|
||||||
|
echo " 4. Click '+' button"
|
||||||
|
echo " 5. Enter: ${SPARKLE_REPO}"
|
||||||
|
echo " 6. Select version ${SPARKLE_VERSION}"
|
||||||
|
echo " 7. Click 'Add Package'"
|
||||||
|
echo " 8. Select 'Sparkle' product and add to Gaze target"
|
||||||
|
echo ""
|
||||||
|
print_info "Or run this from command line:"
|
||||||
|
echo " xed Gaze.xcodeproj"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Note: Programmatically adding Swift packages via pbxproj is extremely complex
|
||||||
|
# and error-prone. Manual addition via Xcode UI is recommended.
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
# Function to create backup directories
|
# Function to create backup directories
|
||||||
create_backup_dirs() {
|
create_backup_dirs() {
|
||||||
mkdir -p "${APPSTORE_CONFIG}"
|
mkdir -p "${APPSTORE_CONFIG}"
|
||||||
@@ -233,6 +345,13 @@ show_current_mode() {
|
|||||||
echo " Build Settings: ${GREEN}Self-Distribution${NC} (no APPSTORE flag)"
|
echo " Build Settings: ${GREEN}Self-Distribution${NC} (no APPSTORE flag)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check for Sparkle package dependency
|
||||||
|
if has_sparkle_package || grep -q "Sparkle" "${PROJECT_FILE}" 2>/dev/null; then
|
||||||
|
echo " Package Dependency: ${GREEN}Self-Distribution${NC} (has Sparkle)"
|
||||||
|
else
|
||||||
|
echo " Package Dependency: ${BLUE}App Store${NC} (no Sparkle)"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,18 +365,17 @@ switch_to_appstore() {
|
|||||||
backup_current_config "self-distribution" "${SELF_CONFIG}"
|
backup_current_config "self-distribution" "${SELF_CONFIG}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Remove Sparkle package dependency
|
||||||
|
remove_sparkle_package
|
||||||
|
echo ""
|
||||||
|
|
||||||
# Restore App Store config
|
# Restore App Store config
|
||||||
restore_config "App Store" "${APPSTORE_CONFIG}"
|
restore_config "App Store" "${APPSTORE_CONFIG}"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
print_success "Switched to App Store distribution mode"
|
print_success "Switched to App Store distribution mode"
|
||||||
print_warning "Remember to add the Run Script phase to remove Sparkle framework!"
|
print_info "Sparkle framework has been removed from dependencies"
|
||||||
echo ""
|
print_info "You can now archive and submit to App Store"
|
||||||
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
|
# Function to switch to self-distribution configuration
|
||||||
@@ -273,9 +391,19 @@ switch_to_self() {
|
|||||||
# Restore self-distribution config
|
# Restore self-distribution config
|
||||||
restore_config "self-distribution" "${SELF_CONFIG}"
|
restore_config "self-distribution" "${SELF_CONFIG}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Add Sparkle package dependency
|
||||||
|
add_sparkle_package
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
print_success "Switched to self-distribution mode"
|
print_success "Switched to self-distribution mode"
|
||||||
print_info "Sparkle auto-updates enabled"
|
print_info "Sparkle auto-updates enabled"
|
||||||
|
|
||||||
|
if ! has_sparkle_package; then
|
||||||
|
echo ""
|
||||||
|
print_warning "Remember to add Sparkle package in Xcode (see instructions above)"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to show usage
|
# Function to show usage
|
||||||
@@ -288,11 +416,13 @@ ${GREEN}Usage:${NC}
|
|||||||
|
|
||||||
${GREEN}Commands:${NC}
|
${GREEN}Commands:${NC}
|
||||||
appstore Switch to App Store distribution configuration
|
appstore Switch to App Store distribution configuration
|
||||||
|
- Removes Sparkle package dependency from project
|
||||||
- Removes Sparkle keys from Info.plist
|
- Removes Sparkle keys from Info.plist
|
||||||
- Removes Sparkle entitlements
|
- Removes Sparkle entitlements
|
||||||
- Adds APPSTORE compiler flag
|
- Adds APPSTORE compiler flag
|
||||||
|
|
||||||
self Switch to self-distribution configuration
|
self Switch to self-distribution configuration
|
||||||
|
- Prompts to add Sparkle package dependency
|
||||||
- Adds Sparkle keys to Info.plist
|
- Adds Sparkle keys to Info.plist
|
||||||
- Adds Sparkle entitlements for XPC services
|
- Adds Sparkle entitlements for XPC services
|
||||||
- Removes APPSTORE compiler flag
|
- Removes APPSTORE compiler flag
|
||||||
|
|||||||
Reference in New Issue
Block a user