diff --git a/Resources/Info.plist b/Resources/Info.plist index a859496..99479b9 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -19,9 +19,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.4 + 1.0.5 CFBundleVersion - 17 + 18 GlossSafariExtensionAvailable CFBundleDocumentTypes diff --git a/Sources/Gloss/BrowserExtensionFiles.swift b/Sources/Gloss/BrowserExtensionFiles.swift index 37cd751..a2eb4cc 100644 --- a/Sources/Gloss/BrowserExtensionFiles.swift +++ b/Sources/Gloss/BrowserExtensionFiles.swift @@ -43,7 +43,9 @@ enum BrowserExtensionFiles { if fileManager.fileExists(atPath: destination.path) { let marker = destination.appendingPathComponent(markerName) - guard fileManager.fileExists(atPath: marker.path) else { + let isManaged = fileManager.fileExists(atPath: marker.path) + guard try isManaged || isRecognizableLegacyCopy(source: source, destination: destination) + else { throw BrowserExtensionError.unmanagedDestination(destination.path) } } @@ -73,6 +75,17 @@ enum BrowserExtensionFiles { return destination } + static func isRecognizableLegacyCopy(source: URL, destination: URL) throws -> Bool { + let sourceFiles = try relativeFiles(in: source).filter { $0 != markerName } + let destinationFiles = try relativeFiles(in: destination).filter { $0 != markerName } + guard sourceFiles == destinationFiles else { return false } + + return FileManager.default.contentsEqual( + atPath: source.appendingPathComponent("manifest.json").path, + andPath: destination.appendingPathComponent("manifest.json").path + ) + } + static func synchronizeManagedCopy(source: URL, destination: URL) throws { let fileManager = FileManager.default let sourceFiles = try relativeFiles(in: source) diff --git a/Tests/GlossAppTests/BrowserExtensionFilesTests.swift b/Tests/GlossAppTests/BrowserExtensionFilesTests.swift index 008c898..bda9e0b 100644 --- a/Tests/GlossAppTests/BrowserExtensionFilesTests.swift +++ b/Tests/GlossAppTests/BrowserExtensionFilesTests.swift @@ -3,6 +3,48 @@ import XCTest @testable import Gloss final class BrowserExtensionFilesTests: XCTestCase { + func testRecognizesUnmarkedBundledCopyForMigration() throws { + let root = FileManager.default.temporaryDirectory + .appendingPathComponent("gloss-extension-migration-\(UUID().uuidString)", isDirectory: true) + let source = root.appendingPathComponent("source", isDirectory: true) + let destination = root.appendingPathComponent("destination", isDirectory: true) + defer { try? FileManager.default.removeItem(at: root) } + + try FileManager.default.createDirectory(at: source, withIntermediateDirectories: true) + try FileManager.default.createDirectory(at: destination, withIntermediateDirectories: true) + let manifest = Data(#"{"name":"Gloss","version":"1.0.0"}"#.utf8) + try manifest.write(to: source.appendingPathComponent("manifest.json")) + try manifest.write(to: destination.appendingPathComponent("manifest.json")) + try Data("new".utf8).write(to: source.appendingPathComponent("background.js")) + try Data("old".utf8).write(to: destination.appendingPathComponent("background.js")) + + XCTAssertTrue( + try BrowserExtensionFiles.isRecognizableLegacyCopy( + source: source, + destination: destination + ) + ) + + try Data(#"{"name":"Other","version":"1.0.0"}"#.utf8).write( + to: destination.appendingPathComponent("manifest.json") + ) + XCTAssertFalse( + try BrowserExtensionFiles.isRecognizableLegacyCopy( + source: source, + destination: destination + ) + ) + + try manifest.write(to: destination.appendingPathComponent("manifest.json")) + try Data("foreign".utf8).write(to: destination.appendingPathComponent("extra.js")) + XCTAssertFalse( + try BrowserExtensionFiles.isRecognizableLegacyCopy( + source: source, + destination: destination + ) + ) + } + func testSynchronizeManagedCopyUpdatesFilesWithoutReplacingDirectory() throws { let root = FileManager.default.temporaryDirectory .appendingPathComponent("gloss-extension-sync-\(UUID().uuidString)", isDirectory: true) diff --git a/docs/release-notes/v1.0.5.md b/docs/release-notes/v1.0.5.md new file mode 100644 index 0000000..70a49a5 --- /dev/null +++ b/docs/release-notes/v1.0.5.md @@ -0,0 +1,11 @@ +# Gloss 1.0.5 + +Gloss 1.0.5 fixes browser extension setup for existing installations. + +## Browser extension + +- Migrates recognizable legacy extension directories that predate the Gloss management marker. +- Preserves overwrite protection for unrelated or modified extension directories. +- Restores the **Show Extension** action without requiring users to delete or reinstall the extension manually. + +Gloss 1.0.5 requires macOS 14 or newer.