From e9ff69b3016e50517d936dde2d3697a61c8dd436 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Tue, 13 Jan 2026 14:25:28 -0500 Subject: [PATCH] fix: switch_to fully handles sparkle --- .../appstore/project_release.txt | 2 +- .../self/project_release.txt | 2 +- .manage_sparkle.py | 131 +++++++++++++++++ Gaze.xcodeproj/project.pbxproj | 1 + Gaze.xcodeproj/project.pbxproj.tmp | 0 .../xcshareddata/swiftpm/Package.resolved | 20 +-- switch_to | 132 ++++++++++++------ 7 files changed, 236 insertions(+), 52 deletions(-) create mode 100755 .manage_sparkle.py delete mode 100644 Gaze.xcodeproj/project.pbxproj.tmp diff --git a/.distribution_configs/appstore/project_release.txt b/.distribution_configs/appstore/project_release.txt index 7091c01..01e0590 100644 --- a/.distribution_configs/appstore/project_release.txt +++ b/.distribution_configs/appstore/project_release.txt @@ -7,7 +7,7 @@ CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 8; DEVELOPMENT_TEAM = 6GK4F9L62V; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; diff --git a/.distribution_configs/self/project_release.txt b/.distribution_configs/self/project_release.txt index d3b7842..ce47072 100644 --- a/.distribution_configs/self/project_release.txt +++ b/.distribution_configs/self/project_release.txt @@ -7,7 +7,7 @@ CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 8; DEVELOPMENT_TEAM = 6GK4F9L62V; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; diff --git a/.manage_sparkle.py b/.manage_sparkle.py new file mode 100755 index 0000000..71cd05d --- /dev/null +++ b/.manage_sparkle.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +""" +Safely add or remove Sparkle package from Xcode project +""" +import sys +import re + +SPARKLE_REPO = "https://github.com/sparkle-project/Sparkle" +SPARKLE_VERSION = "2.8.1" + +# Consistent IDs for Sparkle references +PKG_REF_ID = "27SPARKLE00000000001" +PKG_PROD_ID = "27SPARKLE00000000002" +BUILD_FILE_ID = "27SPARKLE00000000003" + + +def remove_sparkle(pbxproj_path): + """Remove all Sparkle references from project.pbxproj""" + with open(pbxproj_path, 'r') as f: + content = f.read() + + # Remove Sparkle from packageReferences array + content = re.sub(r'\t\t\t\t27SPARKLE\d+ /\* XCRemoteSwiftPackageReference "Sparkle" \*/,\n', '', content) + + # Remove Sparkle from packageProductDependencies array + content = re.sub(r'\t\t\t\t27SPARKLE\d+ /\* Sparkle \*/,\n', '', content) + + # Remove Sparkle from Frameworks build phase + content = re.sub(r'\t\t\t\t27SPARKLE\d+ /\* Sparkle in Frameworks \*/,\n', '', content) + + # Remove PBXBuildFile section for Sparkle + content = re.sub(r'\t\t27SPARKLE\d+ /\* Sparkle in Frameworks \*/ = \{isa = PBXBuildFile; productRef = 27SPARKLE\d+ /\* Sparkle \*/; \};\n', '', content) + + # Remove XCRemoteSwiftPackageReference section (multi-line) + pattern = r'\t\t27SPARKLE\d+ /\* XCRemoteSwiftPackageReference "Sparkle" \*/ = \{\n.*?\n\t\t\};\n' + content = re.sub(pattern, '', content, flags=re.DOTALL) + + # Remove XCSwiftPackageProductDependency section (multi-line) + pattern = r'\t\t27SPARKLE\d+ /\* XCSwiftPackageProductDependency "Sparkle" \*/ = \{\n.*?\n\t\t\};\n' + content = re.sub(pattern, '', content, flags=re.DOTALL) + + with open(pbxproj_path, 'w') as f: + f.write(content) + + print("✓ Removed Sparkle references from project.pbxproj") + return True + + +def add_sparkle(pbxproj_path): + """Add Sparkle package to project.pbxproj""" + with open(pbxproj_path, 'r') as f: + content = f.read() + + # Check if Sparkle already exists + if 'Sparkle' in content: + print("ℹ Sparkle already in project") + return True + + # 1. Add PBXBuildFile for Sparkle (after Lottie, before End section) + pattern = r'(\t\t275915892F132A9200D0E60D /\* Lottie in Frameworks \*/ = \{isa = PBXBuildFile; productRef = 27AE10B12F10B1FC00E00DBC /\* Lottie \*/; \};\n)(/\* End PBXBuildFile section \*/)' + replacement = r'\1\t\t' + BUILD_FILE_ID + r' /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = ' + PKG_PROD_ID + r' /* Sparkle */; };' + '\n' + r'\2' + content = re.sub(pattern, replacement, content) + + # 2. Add Sparkle to Frameworks build phase (after Lottie) + pattern = r'(\t\t\t\t275915892F132A9200D0E60D /\* Lottie in Frameworks \*/,\n)' + replacement = r'\1\t\t\t\t' + BUILD_FILE_ID + r' /* Sparkle in Frameworks */,' + '\n' + content = re.sub(pattern, replacement, content) + + # 3. Add Sparkle to packageProductDependencies (after Lottie) + pattern = r'(packageProductDependencies = \(\n\t\t\t\t27AE10B12F10B1FC00E00DBC /\* Lottie \*/,\n)' + replacement = r'\1\t\t\t\t' + PKG_PROD_ID + r' /* Sparkle */,' + '\n' + content = re.sub(pattern, replacement, content) + + # 4. Add Sparkle to packageReferences (after Lottie) + pattern = r'(packageReferences = \(\n\t\t\t\t27AE10B02F10B1FC00E00DBC /\* XCRemoteSwiftPackageReference "lottie-spm" \*/,\n)' + replacement = r'\1\t\t\t\t' + PKG_REF_ID + r' /* XCRemoteSwiftPackageReference "Sparkle" */,' + '\n' + content = re.sub(pattern, replacement, content) + + # 5. Add XCRemoteSwiftPackageReference section (before End section) + sparkle_ref = f'''\t\t{PKG_REF_ID} /* XCRemoteSwiftPackageReference "Sparkle" */ = {{ +\t\t\tisa = XCRemoteSwiftPackageReference; +\t\t\trepositoryURL = "{SPARKLE_REPO}"; +\t\t\trequirement = {{ +\t\t\t\tkind = exactVersion; +\t\t\t\tversion = {SPARKLE_VERSION}; +\t\t\t}}; +\t\t}}; +''' + pattern = r'(/\* End XCRemoteSwiftPackageReference section \*/)' + replacement = sparkle_ref + r'\1' + content = re.sub(pattern, replacement, content) + + # 6. Add XCSwiftPackageProductDependency section (before End section) + sparkle_prod = f'''\t\t{PKG_PROD_ID} /* XCSwiftPackageProductDependency "Sparkle" */ = {{ +\t\t\tisa = XCSwiftPackageProductDependency; +\t\t\tpackage = {PKG_REF_ID} /* XCRemoteSwiftPackageReference "Sparkle" */; +\t\t\tproductName = Sparkle; +\t\t}}; +''' + pattern = r'(/\* End XCSwiftPackageProductDependency section \*/)' + replacement = sparkle_prod + r'\1' + content = re.sub(pattern, replacement, content) + + with open(pbxproj_path, 'w') as f: + f.write(content) + + print("✓ Added Sparkle references to project.pbxproj") + return True + + +if __name__ == '__main__': + if len(sys.argv) != 3: + print("Usage: .manage_sparkle.py [add|remove] ") + sys.exit(1) + + action = sys.argv[1] + pbxproj_path = sys.argv[2] + + try: + if action == 'add': + success = add_sparkle(pbxproj_path) + elif action == 'remove': + success = remove_sparkle(pbxproj_path) + else: + print(f"Unknown action: {action}") + sys.exit(1) + + sys.exit(0 if success else 1) + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) diff --git a/Gaze.xcodeproj/project.pbxproj b/Gaze.xcodeproj/project.pbxproj index 93d0cee..517fb0c 100644 --- a/Gaze.xcodeproj/project.pbxproj +++ b/Gaze.xcodeproj/project.pbxproj @@ -435,6 +435,7 @@ ); 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; diff --git a/Gaze.xcodeproj/project.pbxproj.tmp b/Gaze.xcodeproj/project.pbxproj.tmp deleted file mode 100644 index e69de29..0000000 diff --git a/Gaze.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Gaze.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index de23afb..726a161 100644 --- a/Gaze.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Gaze.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,15 +1,15 @@ { - "originHash" : "6b3386dc9ff1f3a74f1534de9c41d47137eae0901cfe819ed442f1b241549359", - "pins" : [ + "originHash": "513d974fbede884a919977d3446360023f6e3239ac314f4fbd9657e80aca7560", + "pins": [ { - "identity" : "lottie-spm", - "kind" : "remoteSourceControl", - "location" : "https://github.com/airbnb/lottie-spm.git", - "state" : { - "revision" : "69faaefa7721fba9e434a52c16adf4329c9084db", - "version" : "4.6.0" + "identity": "lottie-spm", + "kind": "remoteSourceControl", + "location": "https://github.com/airbnb/lottie-spm.git", + "state": { + "revision": "69faaefa7721fba9e434a52c16adf4329c9084db", + "version": "4.6.0" } } ], - "version" : 3 -} + "version": 3 +} \ No newline at end of file diff --git a/switch_to b/switch_to index 9c83c8e..7a943bf 100755 --- a/switch_to +++ b/switch_to @@ -64,10 +64,8 @@ remove_sparkle_package() { # 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: @@ -92,23 +90,12 @@ except Exception as e: 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}" + # Use Python script to safely remove Sparkle from project.pbxproj + if ! python3 .manage_sparkle.py remove "${PROJECT_FILE}"; then + print_error "Failed to update project.pbxproj" + mv "${PROJECT_FILE}.backup" "${PROJECT_FILE}" + return 1 + fi rm -f "${PROJECT_FILE}.backup" @@ -126,23 +113,83 @@ add_sparkle_package() { 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 "" + # Backup project file + cp "${PROJECT_FILE}" "${PROJECT_FILE}.backup" - # Note: Programmatically adding Swift packages via pbxproj is extremely complex - # and error-prone. Manual addition via Xcode UI is recommended. + # Add Sparkle to Package.resolved + if [ ! -f "${PACKAGE_RESOLVED}" ]; then + # Create Package.resolved if it doesn't exist + mkdir -p "$(dirname "${PACKAGE_RESOLVED}")" + cat > "${PACKAGE_RESOLVED}" << 'EOF' +{ + "originHash" : "6b3386dc9ff1f3a74f1534de9c41d47137eae0901cfe819ed442f1b241549359", + "pins" : [ + { + "identity" : "lottie-spm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/airbnb/lottie-spm.git", + "state" : { + "revision" : "69faaefa7721fba9e434a52c16adf4329c9084db", + "version" : "4.6.0" + } + } + ], + "version" : 3 +} +EOF + fi + + python3 -c " +import json + +try: + with open('${PACKAGE_RESOLVED}', 'r') as f: + data = json.load(f) + + # Add Sparkle to pins if not present + # Note: We let Xcode resolve the actual revision + sparkle_pin = { + 'identity': 'sparkle', + 'kind': 'remoteSourceControl', + 'location': '${SPARKLE_REPO}', + 'state': { + 'version': '${SPARKLE_VERSION}' + } + } + + if 'pins' not in data: + data['pins'] = [] + + # Check if Sparkle already in pins + if not any(pin.get('identity', '').lower() == 'sparkle' for pin in data['pins']): + data['pins'].append(sparkle_pin) + data['pins'].sort(key=lambda x: x.get('identity', '')) + + 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 + + # Use Python script to safely add Sparkle to project.pbxproj + if ! python3 .manage_sparkle.py add "${PROJECT_FILE}"; then + print_error "Failed to update project.pbxproj" + mv "${PROJECT_FILE}.backup" "${PROJECT_FILE}" + return 1 + fi + + rm -f "${PROJECT_FILE}.backup" + + print_success "Added Sparkle package dependency" + print_info "Run './run build' or open Xcode to resolve packages" return 0 } @@ -206,18 +253,23 @@ restore_config() { 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 + # Add APPSTORE flag to both Debug and Release 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;/{ + # Add to Debug configuration + sed -i.backup '/27A21B5E2F0F69DD0018C4F3 \/\* Debug \*\/ = {/,/name = Debug;/{ /MARKETING_VERSION = /a\ OTHER_SWIFT_FLAGS = "-D APPSTORE"; }' "${PROJECT_FILE}" - rm -f "${PROJECT_FILE}.backup" + # Add to Release configuration + sed -i.backup2 '/27A21B5F2F0F69DD0018C4F3 \/\* Release \*\/ = {/,/name = Release;/{ + /MARKETING_VERSION = /a\ + OTHER_SWIFT_FLAGS = "-D APPSTORE"; + }' "${PROJECT_FILE}" + rm -f "${PROJECT_FILE}.backup" "${PROJECT_FILE}.backup2" print_success "Added APPSTORE compiler flag" fi else - # Remove APPSTORE flag + # Remove APPSTORE flag from both Debug and Release 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"