native macOS codings agent orchestrator
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge remote-tracking branch 'upstream/main'

# Conflicts:
# supacode/Features/Settings/BusinessLogic/RepositorySettingsKey.swift

onevcat f7b35efa d7aa37da

+119 -90
+4 -1
.github/workflows/release-tip.yml
··· 131 131 SENTRY_FRAMEWORK="$APP_PATH/Contents/Frameworks/Sentry.framework" 132 132 codesign -f -s "$DEVELOPER_ID_IDENTITY_SHA" -o runtime --timestamp -v "$SENTRY_FRAMEWORK/Versions/A/Sentry" 133 133 codesign -f -s "$DEVELOPER_ID_IDENTITY_SHA" -o runtime --timestamp -v "$SENTRY_FRAMEWORK" 134 - codesign -f -s "$DEVELOPER_ID_IDENTITY_SHA" -o runtime --timestamp -v "$APP_PATH" 134 + codesign -f -s "$DEVELOPER_ID_IDENTITY_SHA" -o runtime --timestamp --preserve-metadata=entitlements,requirements,flags -v "$APP_PATH" 135 + 136 + codesign -d --entitlements - "$APP_PATH/Contents/MacOS/supacode" 2>&1 | tee /tmp/supacode-entitlements.txt 137 + grep -q "com.apple.security.device.audio-input" /tmp/supacode-entitlements.txt 135 138 136 139 codesign -vvv --deep --strict "$APP_PATH" 137 140 - name: Store notarization credentials
+4 -1
.github/workflows/release.yml
··· 127 127 SENTRY_FRAMEWORK="$APP_PATH/Contents/Frameworks/Sentry.framework" 128 128 codesign -f -s "$DEVELOPER_ID_IDENTITY_SHA" -o runtime --timestamp -v "$SENTRY_FRAMEWORK/Versions/A/Sentry" 129 129 codesign -f -s "$DEVELOPER_ID_IDENTITY_SHA" -o runtime --timestamp -v "$SENTRY_FRAMEWORK" 130 - codesign -f -s "$DEVELOPER_ID_IDENTITY_SHA" -o runtime --timestamp -v "$APP_PATH" 130 + codesign -f -s "$DEVELOPER_ID_IDENTITY_SHA" -o runtime --timestamp --preserve-metadata=entitlements,requirements,flags -v "$APP_PATH" 131 + 132 + codesign -d --entitlements - "$APP_PATH/Contents/MacOS/supacode" 2>&1 | tee /tmp/supacode-entitlements.txt 133 + grep -q "com.apple.security.device.audio-input" /tmp/supacode-entitlements.txt 131 134 132 135 codesign -vvv --deep --strict "$APP_PATH" 133 136 codesign -dv --verbose=4 "$APP_PATH" 2>&1 | grep -E "Authority=Developer ID Application|Timestamp="
+1 -1
Makefile
··· 40 40 rsync -a --delete "$$terminfo_src/" "$$terminfo_dst/" 41 41 42 42 build-app: build-ghostty-xcframework # Build the macOS app (Debug) 43 - bash -o pipefail -c 'xcodebuild -project supacode.xcodeproj -scheme supacode -configuration Debug build CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY="" -skipMacroValidation -clonedSourcePackagesDirPath $(SPM_CACHE_DIR) 2>&1 | mise exec -- xcsift -qw --format toon' 43 + bash -o pipefail -c 'xcodebuild -project supacode.xcodeproj -scheme supacode -configuration Debug build -skipMacroValidation -clonedSourcePackagesDirPath $(SPM_CACHE_DIR) 2>&1 | mise exec -- xcsift -qw --format toon' 44 44 45 45 run-app: build-app # Build then launch (Debug) with log streaming 46 46 @settings="$$(xcodebuild -project supacode.xcodeproj -scheme supacode -configuration Debug -showBuildSettings -json 2>/dev/null)"; \
+14 -12
supacode.xcodeproj/project.pbxproj
··· 431 431 buildSettings = { 432 432 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 433 433 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 434 - AUTOMATION_APPLE_EVENTS = YES; 435 - CODE_SIGN_STYLE = Automatic; 434 + AUTOMATION_APPLE_EVENTS = YES; 435 + CODE_SIGN_ENTITLEMENTS = supacode/supacodeDebug.entitlements; 436 + CODE_SIGN_STYLE = Automatic; 436 437 COMBINE_HIDPI_IMAGES = YES; 437 438 COMPILATION_CACHE_ENABLE_CACHING = YES; 438 - CURRENT_PROJECT_VERSION = 108; 439 + CURRENT_PROJECT_VERSION = 110; 439 440 DEAD_CODE_STRIPPING = YES; 440 441 DEBUG_INFORMATION_FORMAT = dwarf; 441 442 DEVELOPMENT_TEAM = ""; ··· 457 458 "@executable_path/../Frameworks", 458 459 ); 459 460 MACOSX_DEPLOYMENT_TARGET = 26.0; 460 - MARKETING_VERSION = 0.6.3; 461 + MARKETING_VERSION = 0.6.5; 461 462 OTHER_LDFLAGS = ( 462 463 "$(inherited)", 463 464 "-lc++", ··· 486 487 buildSettings = { 487 488 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 488 489 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 489 - AUTOMATION_APPLE_EVENTS = YES; 490 - CODE_SIGN_STYLE = Automatic; 490 + AUTOMATION_APPLE_EVENTS = YES; 491 + CODE_SIGN_ENTITLEMENTS = supacode/supacode.entitlements; 492 + CODE_SIGN_STYLE = Automatic; 491 493 COMBINE_HIDPI_IMAGES = YES; 492 494 COMPILATION_CACHE_ENABLE_CACHING = NO; 493 - CURRENT_PROJECT_VERSION = 108; 495 + CURRENT_PROJECT_VERSION = 110; 494 496 DEAD_CODE_STRIPPING = YES; 495 497 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 496 498 DEVELOPMENT_TEAM = ""; ··· 512 514 "@executable_path/../Frameworks", 513 515 ); 514 516 MACOSX_DEPLOYMENT_TARGET = 26.0; 515 - MARKETING_VERSION = 0.6.3; 517 + MARKETING_VERSION = 0.6.5; 516 518 OTHER_LDFLAGS = ( 517 519 "$(inherited)", 518 520 "-lc++", ··· 541 543 buildSettings = { 542 544 BUNDLE_LOADER = "$(TEST_HOST)"; 543 545 CODE_SIGN_STYLE = Automatic; 544 - CURRENT_PROJECT_VERSION = 108; 546 + CURRENT_PROJECT_VERSION = 110; 545 547 DEAD_CODE_STRIPPING = YES; 546 548 DEVELOPMENT_TEAM = ""; 547 549 GENERATE_INFOPLIST_FILE = YES; 548 550 MACOSX_DEPLOYMENT_TARGET = 26.1; 549 - MARKETING_VERSION = 0.6.3; 551 + MARKETING_VERSION = 0.6.5; 550 552 PRODUCT_BUNDLE_IDENTIFIER = app.supabit.supacodeTests; 551 553 PRODUCT_NAME = "$(TARGET_NAME)"; 552 554 STRING_CATALOG_GENERATE_SYMBOLS = NO; ··· 563 565 buildSettings = { 564 566 BUNDLE_LOADER = "$(TEST_HOST)"; 565 567 CODE_SIGN_STYLE = Automatic; 566 - CURRENT_PROJECT_VERSION = 108; 568 + CURRENT_PROJECT_VERSION = 110; 567 569 DEAD_CODE_STRIPPING = YES; 568 570 DEVELOPMENT_TEAM = ""; 569 571 GENERATE_INFOPLIST_FILE = YES; 570 572 MACOSX_DEPLOYMENT_TARGET = 26.1; 571 - MARKETING_VERSION = 0.6.3; 573 + MARKETING_VERSION = 0.6.5; 572 574 PRODUCT_BUNDLE_IDENTIFIER = app.supabit.supacodeTests; 573 575 PRODUCT_NAME = "$(TARGET_NAME)"; 574 576 STRING_CATALOG_GENERATE_SYMBOLS = NO;
+25 -22
supacode/Features/Settings/BusinessLogic/RepositorySettingsKey.swift
··· 61 61 } 62 62 63 63 @Shared(.settingsFile) var settingsFile: SettingsFile 64 - let migratedSettings = $settingsFile.withLock { settings in 65 - settings.repositories[repositoryID] ?? (context.initialValue ?? .default) 66 - } 67 - do { 68 - let encoder = JSONEncoder() 69 - encoder.outputFormatting = [.prettyPrinted, .sortedKeys] 70 - let data = try encoder.encode(migratedSettings) 71 - try repositoryLocalSettingsStorage.save(data, repositorySettingsURL) 72 - } catch { 73 - let path = repositorySettingsURL.path(percentEncoded: false) 74 - SupaLogger("Settings").warning( 75 - "Unable to write migrated repository settings to \(path): \(error.localizedDescription)" 76 - ) 64 + let settings = $settingsFile.withLock { settings in 65 + if let existing = settings.repositories[repositoryID] { 66 + return existing 67 + } 68 + let defaults = context.initialValue ?? .default 69 + settings.repositories[repositoryID] = defaults 70 + return defaults 77 71 } 78 - continuation.resume(returning: migratedSettings) 72 + continuation.resume(returning: settings) 79 73 } 80 74 81 75 func subscribe( ··· 92 86 ) { 93 87 @Dependency(\.repositoryLocalSettingsStorage) var repositoryLocalSettingsStorage 94 88 let repositorySettingsURL = SupacodePaths.repositorySettingsURL(for: rootURL) 95 - do { 96 - let encoder = JSONEncoder() 97 - encoder.outputFormatting = [.prettyPrinted, .sortedKeys] 98 - let data = try encoder.encode(value) 99 - try repositoryLocalSettingsStorage.save(data, repositorySettingsURL) 100 - continuation.resume() 101 - } catch { 102 - continuation.resume(throwing: error) 89 + if (try? repositoryLocalSettingsStorage.load(repositorySettingsURL)) != nil { 90 + do { 91 + let encoder = JSONEncoder() 92 + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] 93 + let data = try encoder.encode(value) 94 + try repositoryLocalSettingsStorage.save(data, repositorySettingsURL) 95 + continuation.resume() 96 + } catch { 97 + continuation.resume(throwing: error) 98 + } 99 + return 100 + } 101 + 102 + @Shared(.settingsFile) var settingsFile: SettingsFile 103 + $settingsFile.withLock { 104 + $0.repositories[repositoryID] = value 103 105 } 106 + continuation.resume() 104 107 } 105 108 } 106 109
+20
supacode/supacode.entitlements
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 + <plist version="1.0"> 4 + <dict> 5 + <key>com.apple.security.automation.apple-events</key> 6 + <true/> 7 + <key>com.apple.security.device.audio-input</key> 8 + <true/> 9 + <key>com.apple.security.device.camera</key> 10 + <true/> 11 + <key>com.apple.security.personal-information.addressbook</key> 12 + <true/> 13 + <key>com.apple.security.personal-information.calendars</key> 14 + <true/> 15 + <key>com.apple.security.personal-information.location</key> 16 + <true/> 17 + <key>com.apple.security.personal-information.photos-library</key> 18 + <true/> 19 + </dict> 20 + </plist>
+22
supacode/supacodeDebug.entitlements
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 + <plist version="1.0"> 4 + <dict> 5 + <key>com.apple.security.automation.apple-events</key> 6 + <true/> 7 + <key>com.apple.security.cs.disable-library-validation</key> 8 + <true/> 9 + <key>com.apple.security.device.audio-input</key> 10 + <true/> 11 + <key>com.apple.security.device.camera</key> 12 + <true/> 13 + <key>com.apple.security.personal-information.addressbook</key> 14 + <true/> 15 + <key>com.apple.security.personal-information.calendars</key> 16 + <true/> 17 + <key>com.apple.security.personal-information.location</key> 18 + <true/> 19 + <key>com.apple.security.personal-information.photos-library</key> 20 + <true/> 21 + </dict> 22 + </plist>
+29 -53
supacodeTests/RepositorySettingsKeyTests.swift
··· 14 14 #expect(!json.contains("worktreeBaseRef")) 15 15 } 16 16 17 - @Test(.dependencies) func loadCreatesDefaultAndMigratesToLocal() throws { 18 - let globalStorage = SettingsTestStorage() 19 - let localStorage = RepositoryLocalSettingsTestStorage() 17 + @Test(.dependencies) func loadCreatesDefaultAndPersists() throws { 18 + let storage = SettingsTestStorage() 20 19 let rootURL = URL(fileURLWithPath: "/tmp/repo") 21 - let settingsFileURL = URL(fileURLWithPath: "/tmp/supacode-settings-\(UUID().uuidString).json") 22 - let repositoryID = rootURL.standardizedFileURL.path(percentEncoded: false) 23 - let localURL = SupacodePaths.repositorySettingsURL(for: rootURL) 24 20 25 - let loaded = withDependencies { 26 - $0.settingsFileStorage = globalStorage.storage 27 - $0.settingsFileURL = settingsFileURL 28 - $0.repositoryLocalSettingsStorage = localStorage.storage 21 + let settings = withDependencies { 22 + $0.settingsFileStorage = storage.storage 29 23 } operation: { 30 24 @Shared(.repositorySettings(rootURL)) var repositorySettings: RepositorySettings 31 25 return repositorySettings 32 26 } 33 27 34 - #expect(loaded == .default) 28 + #expect(settings == RepositorySettings.default) 35 29 36 - let localData = try #require(localStorage.data(at: localURL)) 37 - let localDecoded = try JSONDecoder().decode(RepositorySettings.self, from: localData) 38 - #expect(localDecoded == .default) 39 - 40 - let globalSaved: SettingsFile = withDependencies { 41 - $0.settingsFileStorage = globalStorage.storage 42 - $0.settingsFileURL = settingsFileURL 43 - $0.repositoryLocalSettingsStorage = localStorage.storage 30 + let saved: SettingsFile = withDependencies { 31 + $0.settingsFileStorage = storage.storage 44 32 } operation: { 45 - @Shared(.settingsFile) var settingsFile: SettingsFile 46 - return settingsFile 33 + @Shared(.settingsFile) var settings: SettingsFile 34 + return settings 47 35 } 48 36 49 - #expect(globalSaved.repositories[repositoryID] == nil) 37 + #expect( 38 + saved.repositories[rootURL.path(percentEncoded: false)] == RepositorySettings.default 39 + ) 50 40 } 51 41 52 - @Test(.dependencies) func saveOverwritesExistingSettingsInLocalFile() throws { 53 - let globalStorage = SettingsTestStorage() 54 - let localStorage = RepositoryLocalSettingsTestStorage() 42 + @Test(.dependencies) func saveOverwritesExistingSettings() throws { 43 + let storage = SettingsTestStorage() 55 44 let rootURL = URL(fileURLWithPath: "/tmp/repo") 56 - let settingsFileURL = URL(fileURLWithPath: "/tmp/supacode-settings-\(UUID().uuidString).json") 57 - let localURL = SupacodePaths.repositorySettingsURL(for: rootURL) 58 - 59 - try localStorage.save(encode(.default), at: localURL) 60 45 61 46 var updated = RepositorySettings.default 62 47 updated.runScript = "echo updated" 63 48 64 49 withDependencies { 65 - $0.settingsFileStorage = globalStorage.storage 66 - $0.settingsFileURL = settingsFileURL 67 - $0.repositoryLocalSettingsStorage = localStorage.storage 50 + $0.settingsFileStorage = storage.storage 68 51 } operation: { 69 52 @Shared(.repositorySettings(rootURL)) var repositorySettings: RepositorySettings 70 53 $repositorySettings.withLock { ··· 72 55 } 73 56 } 74 57 75 - let localData = try #require(localStorage.data(at: localURL)) 76 - let localDecoded = try JSONDecoder().decode(RepositorySettings.self, from: localData) 77 - #expect(localDecoded == updated) 58 + let reloaded: SettingsFile = withDependencies { 59 + $0.settingsFileStorage = storage.storage 60 + } operation: { 61 + @Shared(.settingsFile) var settings: SettingsFile 62 + return settings 63 + } 64 + 65 + #expect(reloaded.repositories[rootURL.path(percentEncoded: false)] == updated) 78 66 } 79 67 80 68 @Test func decodeMissingArchiveScriptDefaultsToEmpty() throws { ··· 131 119 #expect(loaded == localSettings) 132 120 } 133 121 134 - @Test(.dependencies) func loadMigratesGlobalWhenLocalMissing() throws { 122 + @Test(.dependencies) func loadFallsBackToGlobalWhenLocalMissing() throws { 135 123 let globalStorage = SettingsTestStorage() 136 124 let localStorage = RepositoryLocalSettingsTestStorage() 137 125 let rootURL = URL(fileURLWithPath: "/tmp/repo") 138 126 let settingsFileURL = URL(fileURLWithPath: "/tmp/supacode-settings-\(UUID().uuidString).json") 139 127 let repositoryID = rootURL.standardizedFileURL.path(percentEncoded: false) 140 - let localURL = SupacodePaths.repositorySettingsURL(for: rootURL) 141 128 var globalSettings = RepositorySettings.default 142 129 globalSettings.runScript = "echo global" 143 130 ··· 162 149 } 163 150 164 151 #expect(loaded == globalSettings) 165 - 166 - let localData = try #require(localStorage.data(at: localURL)) 167 - let localDecoded = try JSONDecoder().decode(RepositorySettings.self, from: localData) 168 - #expect(localDecoded == globalSettings) 169 152 } 170 153 171 - @Test(.dependencies) func loadMigratesGlobalWhenLocalInvalid() throws { 154 + @Test(.dependencies) func loadFallsBackToGlobalWhenLocalInvalid() throws { 172 155 let globalStorage = SettingsTestStorage() 173 156 let localStorage = RepositoryLocalSettingsTestStorage() 174 157 let rootURL = URL(fileURLWithPath: "/tmp/repo") ··· 201 184 } 202 185 203 186 #expect(loaded == globalSettings) 204 - 205 - let localData = try #require(localStorage.data(at: localURL)) 206 - let localDecoded = try JSONDecoder().decode(RepositorySettings.self, from: localData) 207 - #expect(localDecoded == globalSettings) 208 187 } 209 188 210 189 @Test(.dependencies) func loadMigratesLegacyRepositoryRootFile() throws { ··· 275 254 #expect(globalSaved.repositories[repositoryID] == nil) 276 255 } 277 256 278 - @Test(.dependencies) func saveWritesLocalWhenLocalFileMissing() throws { 257 + @Test(.dependencies) func saveWritesGlobalWhenLocalFileMissing() throws { 279 258 let globalStorage = SettingsTestStorage() 280 259 let localStorage = RepositoryLocalSettingsTestStorage() 281 260 let rootURL = URL(fileURLWithPath: "/tmp/repo") ··· 284 263 let localURL = SupacodePaths.repositorySettingsURL(for: rootURL) 285 264 286 265 var updated = RepositorySettings.default 287 - updated.runScript = "echo local" 266 + updated.runScript = "echo global" 288 267 289 268 withDependencies { 290 269 $0.settingsFileStorage = globalStorage.storage ··· 297 276 } 298 277 } 299 278 300 - let localData = try #require(localStorage.data(at: localURL)) 301 - let localDecoded = try JSONDecoder().decode(RepositorySettings.self, from: localData) 302 - #expect(localDecoded == updated) 303 - 304 279 let globalSaved: SettingsFile = withDependencies { 305 280 $0.settingsFileStorage = globalStorage.storage 306 281 $0.settingsFileURL = settingsFileURL ··· 310 285 return settingsFile 311 286 } 312 287 313 - #expect(globalSaved.repositories[repositoryID] == nil) 288 + #expect(globalSaved.repositories[repositoryID] == updated) 289 + #expect(localStorage.data(at: localURL) == nil) 314 290 } 315 291 316 292 private func encode(_ settings: RepositorySettings) throws -> Data {