diff --git a/.gitignore b/.gitignore index a00dbb6..449b38f 100644 --- a/.gitignore +++ b/.gitignore @@ -119,3 +119,4 @@ GIT_SYNC_PHASE1_COMPLETE.md build-dmg.sh .claude/ *.sh +RELEASE_NOTES.md diff --git a/validate_sync_phase1.swift b/validate_sync_phase1.swift deleted file mode 100755 index e79ea65..0000000 --- a/validate_sync_phase1.swift +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/swift -// -// Git Sync Phase 1 Validation Script -// Tests integration without requiring git repository -// - -import Foundation - -print("๐Ÿงช Git Sync Phase 1 Validation") -print("================================\n") - -var passCount = 0 -var failCount = 0 - -func test(_ name: String, _ block: () throws -> Bool) { - do { - if try block() { - print("โœ… \(name)") - passCount += 1 - } else { - print("โŒ \(name)") - failCount += 1 - } - } catch { - print("โŒ \(name) - Error: \(error)") - failCount += 1 - } -} - -// Test 1: SyncModels.swift exists and has correct structure -test("SyncModels.swift exists") { - let path = "oAI/Models/SyncModels.swift" - return FileManager.default.fileExists(atPath: path) -} - -test("SyncModels.swift contains SyncAuthMethod enum") { - let path = "oAI/Models/SyncModels.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("enum SyncAuthMethod") && - content.contains("case ssh") && - content.contains("case password") && - content.contains("case token") -} - -test("SyncModels.swift contains SyncError enum") { - let path = "oAI/Models/SyncModels.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("enum SyncError") && - content.contains("case notConfigured") && - content.contains("case secretsDetected") -} - -test("SyncModels.swift contains SyncStatus struct") { - let path = "oAI/Models/SyncModels.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("struct SyncStatus") && - content.contains("var lastSyncTime") && - content.contains("var isCloned") -} - -test("SyncModels.swift contains ConversationExport struct") { - let path = "oAI/Models/SyncModels.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("struct ConversationExport") && - content.contains("func toMarkdown()") -} - -// Test 2: GitSyncService.swift exists and has correct structure -test("GitSyncService.swift exists") { - let path = "oAI/Services/GitSyncService.swift" - return FileManager.default.fileExists(atPath: path) -} - -test("GitSyncService.swift has singleton pattern") { - let path = "oAI/Services/GitSyncService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("static let shared") && - content.contains("@Observable") -} - -test("GitSyncService.swift has core git operations") { - let path = "oAI/Services/GitSyncService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("func testConnection()") && - content.contains("func cloneRepository()") && - content.contains("func pull()") && - content.contains("func push(") -} - -test("GitSyncService.swift has export/import operations") { - let path = "oAI/Services/GitSyncService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("func exportAllConversations()") && - content.contains("func importAllConversations()") -} - -test("GitSyncService.swift has secret scanning") { - let path = "oAI/Services/GitSyncService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("func scanForSecrets") && - content.contains("OpenAI Key") && - content.contains("Anthropic Key") -} - -test("GitSyncService.swift has status management") { - let path = "oAI/Services/GitSyncService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("func updateStatus()") && - content.contains("syncStatus") -} - -// Test 3: SettingsService.swift has sync properties -test("SettingsService.swift has syncEnabled property") { - let path = "oAI/Services/SettingsService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("var syncEnabled: Bool") -} - -test("SettingsService.swift has sync configuration properties") { - let path = "oAI/Services/SettingsService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("var syncRepoURL") && - content.contains("var syncLocalPath") && - content.contains("var syncAuthMethod") -} - -test("SettingsService.swift has encrypted credential properties") { - let path = "oAI/Services/SettingsService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("var syncUsername: String?") && - content.contains("var syncPassword: String?") && - content.contains("var syncAccessToken: String?") && - content.contains("getEncryptedSetting") && - content.contains("setEncryptedSetting") -} - -test("SettingsService.swift has auto-sync toggles") { - let path = "oAI/Services/SettingsService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("var syncAutoExport") && - content.contains("var syncAutoPull") -} - -test("SettingsService.swift has syncConfigured computed property") { - let path = "oAI/Services/SettingsService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("var syncConfigured: Bool") -} - -// Test 4: SettingsView.swift has Sync tab -test("SettingsView.swift has Sync tab state variables") { - let path = "oAI/Views/Screens/SettingsView.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("@State private var syncRepoURL") && - content.contains("@State private var syncLocalPath") && - content.contains("@State private var syncUsername") && - content.contains("@State private var isTestingSync") -} - -test("SettingsView.swift has syncTab view") { - let path = "oAI/Views/Screens/SettingsView.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("private var syncTab: some View") -} - -test("SettingsView.swift has sync helper methods") { - let path = "oAI/Views/Screens/SettingsView.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("private func testSyncConnection()") && - content.contains("private func cloneRepo()") && - content.contains("private func exportConversations()") && - content.contains("private func pushToGit()") && - content.contains("private func pullFromGit()") -} - -test("SettingsView.swift has sync status properties") { - let path = "oAI/Views/Screens/SettingsView.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("private var syncStatusIcon") && - content.contains("private var syncStatusColor") && - content.contains("private var syncStatusText") -} - -test("SettingsView.swift has Sync tab in picker") { - let path = "oAI/Views/Screens/SettingsView.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("Text(\"Sync\")") && content.contains(".tag(4)") -} - -// Test 5: Build succeeded -test("Project builds successfully") { - return true // Already verified in previous build -} - -// Test 6: File structure validation -test("All sync files are in correct locations") { - let models = FileManager.default.fileExists(atPath: "oAI/Models/SyncModels.swift") - let service = FileManager.default.fileExists(atPath: "oAI/Services/GitSyncService.swift") - return models && service -} - -test("GitSyncService uses correct DatabaseService methods") { - let path = "oAI/Services/GitSyncService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("listConversations()") && - content.contains("loadConversation(id:") -} - -test("GitSyncService handles async operations correctly") { - let path = "oAI/Services/GitSyncService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("async throws") && - content.contains("await runGit") -} - -test("Secret scanning patterns are comprehensive") { - let path = "oAI/Services/GitSyncService.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - let patterns = [ - "OpenAI Key", - "Anthropic Key", - "Bearer Token", - "API Key", - "Access Token" - ] - return patterns.allSatisfy { content.contains($0) } -} - -test("ConversationExport markdown format includes metadata") { - let path = "oAI/Models/SyncModels.swift" - guard let content = try? String(contentsOfFile: path) else { return false } - return content.contains("func toMarkdown()") && - content.contains("Created") && - content.contains("Updated") -} - -// Print summary -print("\n================================") -print("๐Ÿ“Š Test Results") -print("================================") -print("โœ… Passed: \(passCount)") -print("โŒ Failed: \(failCount)") -print("๐Ÿ“ˆ Total: \(passCount + failCount)") -print("๐ŸŽฏ Success Rate: \(passCount * 100 / (passCount + failCount))%") - -if failCount == 0 { - print("\n๐ŸŽ‰ All tests passed! Phase 1 integration is complete.") - exit(0) -} else { - print("\nโš ๏ธ Some tests failed. Review the results above.") - exit(1) -}