diff --git a/Resources/SampleAppcast.xml b/Resources/SampleAppcast.xml index 2a4a2df581..dfc2b3db63 100755 --- a/Resources/SampleAppcast.xml +++ b/Resources/SampleAppcast.xml @@ -6,35 +6,39 @@ en Version 2.0 (2 bugs fixed; 3 new features) + http://sparkle-project.org + 2.0 http://you.com/app/2.0.html - http://sparkle-project.org Wed, 09 Jan 2006 19:20:11 +0000 - + 10.11 Version 1.5 (8 bugs fixed; 2 new features) + http://sparkle-project.org + 1.5 http://you.com/app/1.5.html - http://sparkle-project.org Wed, 01 Jan 2006 12:20:11 +0000 - + 10.11 Version 1.4 (5 bugs fixed; 2 new features) + http://sparkle-project.org http://you.com/app/1.4.html - http://sparkle-project.org + 241 + 1.4 Wed, 25 Dec 2005 12:20:11 +0000 - + 10.11 diff --git a/Sparkle/SUAppcastItem.m b/Sparkle/SUAppcastItem.m index b7aaee4709..e1fd3b89ac 100644 --- a/Sparkle/SUAppcastItem.m +++ b/Sparkle/SUAppcastItem.m @@ -312,23 +312,28 @@ - (nullable instancetype)initWithDictionary:(NSDictionary *)dict relativeToURL:( { self = [super init]; if (self) { + _title = [(NSString *)[dict objectForKey:SURSSElementTitle] copy]; + NSDictionary *enclosure = [dict objectForKey:SURSSElementEnclosure]; // Try to find a version string. - // Finding the new version number from the RSS feed is a little bit hacky. There are two ways: + // Finding the new version number from the RSS feed is a little bit hacky. There are a few ways: // 1. A "sparkle:version" attribute on the enclosure tag, an extension from the RSS spec. - // 2. If there isn't a version attribute, Sparkle will parse the path in the enclosure, expecting + // 2. If there isn't a version attribute, see if there is a version element (this is now the recommended path). + // 3. If there isn't a version element, Sparkle will parse the path in the enclosure, expecting // that it will look like this: http://something.com/YourApp_0.5.zip. It'll read whatever's between the last // underscore and the last period as the version number. So name your packages like this: APPNAME_VERSION.extension. // The big caveat with this is that you can't have underscores in your version strings, as that'll confuse Sparkle. // Feel free to change the separator string to a hyphen or something more suited to your needs if you like. NSString *newVersion = [enclosure objectForKey:SUAppcastAttributeVersion]; if (newVersion == nil) { - newVersion = [dict objectForKey:SUAppcastAttributeVersion]; // Get version from the item, in case it's a download-less item (i.e. paid upgrade). + // Get version from the item + newVersion = [dict objectForKey:SUAppcastElementVersion]; } - if (newVersion == nil) // no sparkle:version attribute anywhere? + if (newVersion == nil) { - SULog(SULogLevelError, @"warning: <%@> for URL '%@' is missing %@ attribute. Version comparison may be unreliable. Please always specify %@", SURSSElementEnclosure, [enclosure objectForKey:SURSSAttributeURL], SUAppcastAttributeVersion, SUAppcastAttributeVersion); + // No sparkle:version element/attribute anywhere? + SULog(SULogLevelError, @"warning: Item '%@' is missing '<%@>' element. Version comparison may be unreliable. Please always specify %@", _title, SUAppcastElementVersion, SUAppcastElementVersion); // Separate the url by underscores and take the last component, as that'll be closest to the end, // then we remove the extension. Hopefully, this will be the version. @@ -340,13 +345,12 @@ - (nullable instancetype)initWithDictionary:(NSDictionary *)dict relativeToURL:( if (!newVersion) { if (error) { - *error = [NSString stringWithFormat:@"Feed item lacks %@ attribute, and version couldn't be deduced from file name (would have used last component of a file name like AppName_1.3.4.zip)", SUAppcastAttributeVersion]; + *error = [NSString stringWithFormat:@"Feed item lacks %@ element, and version couldn't be deduced from file name (would have used last component of a file name like AppName_1.3.4.zip)", SUAppcastElementVersion]; } return nil; } _propertiesDictionary = [[NSDictionary alloc] initWithDictionary:dict]; - _title = [(NSString *)[dict objectForKey:SURSSElementTitle] copy]; _dateString = [(NSString *)[dict objectForKey:SURSSElementPubDate] copy]; _itemDescription = [(NSString *)[dict objectForKey:SURSSElementDescription] copy]; diff --git a/Sparkle/SUConstants.h b/Sparkle/SUConstants.h index a1e65b6679..a1f2669c52 100644 --- a/Sparkle/SUConstants.h +++ b/Sparkle/SUConstants.h @@ -77,6 +77,8 @@ extern NSString *const SUAppcastAttributeVersion; extern NSString *const SUAppcastAttributeOsType; extern NSString *const SUAppcastAttributeInstallationType; +extern NSString *const SUAppcastElementVersion; +extern NSString *const SUAppcastElementShortVersionString; extern NSString *const SUAppcastElementCriticalUpdate; extern NSString *const SUAppcastElementDeltas; extern NSString *const SUAppcastElementMinimumAutoupdateVersion; diff --git a/Sparkle/SUConstants.m b/Sparkle/SUConstants.m index f1aad287cd..d1c75802d1 100644 --- a/Sparkle/SUConstants.m +++ b/Sparkle/SUConstants.m @@ -73,6 +73,8 @@ NSString *const SUAppcastAttributeOsType = @"sparkle:os"; NSString *const SUAppcastAttributeInstallationType = @"sparkle:installationType"; +NSString *const SUAppcastElementVersion = SUAppcastAttributeVersion; +NSString *const SUAppcastElementShortVersionString = SUAppcastAttributeShortVersionString; NSString *const SUAppcastElementCriticalUpdate = @"sparkle:criticalUpdate"; NSString *const SUAppcastElementDeltas = @"sparkle:deltas"; NSString *const SUAppcastElementMinimumAutoupdateVersion = @"sparkle:minimumAutoupdateVersion"; diff --git a/TestApplication/sparkletestcast.xml b/TestApplication/sparkletestcast.xml index b74b0220e8..ebb17b93dd 100644 --- a/TestApplication/sparkletestcast.xml +++ b/TestApplication/sparkletestcast.xml @@ -6,6 +6,7 @@ en Version 2.0 + 2.0 @@ -17,7 +18,7 @@ ]]> Sat, 26 Jul 2014 15:20:11 +0000 - + diff --git a/Tests/Resources/testappcast.xml b/Tests/Resources/testappcast.xml index f78862d1d2..5922b83042 100644 --- a/Tests/Resources/testappcast.xml +++ b/Tests/Resources/testappcast.xml @@ -6,26 +6,26 @@ Version 2.0 desc Sat, 26 Jul 2014 15:20:11 +0000 + Version 3.0 + 3.0 86400 - + Version 4.0 + 4.0 Sat, 26 Jul 2014 15:20:13 +0000 - + 17.0.0 Version 5.0 - + 5.0 + 2.0.0 diff --git a/Tests/Resources/testappcast_info_updates.xml b/Tests/Resources/testappcast_info_updates.xml index e1e5074e17..a8a86a3d03 100644 --- a/Tests/Resources/testappcast_info_updates.xml +++ b/Tests/Resources/testappcast_info_updates.xml @@ -23,19 +23,18 @@ Version 4.0 Sat, 26 Jul 2014 15:20:13 +0000 - + 4.0 + http://sparkle-project.org - - For unit test only Version 3.0 + 3.0 2.0 - + Version 2.0 - + 2.0 + diff --git a/Tests/Resources/testlocalizedreleasenotesappcast.xml b/Tests/Resources/testlocalizedreleasenotesappcast.xml index ee21cd0aa9..5e0d3f7e89 100644 --- a/Tests/Resources/testlocalizedreleasenotesappcast.xml +++ b/Tests/Resources/testlocalizedreleasenotesappcast.xml @@ -5,8 +5,9 @@ Version 6.0 + 6.0 Sat, 26 Jul 2019 15:20:13 +0000 - + https://sparkle-project.org/#works https://sparkle-project.org/#localized_notes_link_works diff --git a/Tests/SUAppcastTest.swift b/Tests/SUAppcastTest.swift index d3c25fd9c4..b593a098fe 100644 --- a/Tests/SUAppcastTest.swift +++ b/Tests/SUAppcastTest.swift @@ -30,6 +30,7 @@ class SUAppcastTest: XCTestCase { XCTAssertEqual("desc", items[0].itemDescription) XCTAssertEqual("Sat, 26 Jul 2014 15:20:11 +0000", items[0].dateString) XCTAssertTrue(items[0].isCriticalUpdate) + XCTAssertEqual(items[0].versionString, "2.0") // This is the best release matching our system version XCTAssertEqual("Version 3.0", items[1].title) @@ -37,6 +38,7 @@ class SUAppcastTest: XCTestCase { XCTAssertNil(items[1].dateString) XCTAssertTrue(items[1].isCriticalUpdate) XCTAssertEqual(items[1].phasedRolloutInterval, 86400) + XCTAssertEqual(items[1].versionString, "3.0") XCTAssertEqual("Version 4.0", items[2].title) XCTAssertNil(items[2].itemDescription) @@ -58,6 +60,7 @@ class SUAppcastTest: XCTestCase { XCTAssertEqual(bestAppcastItem, items[1]) XCTAssertEqual(deltaItem!.fileURL!.lastPathComponent, "3.0_from_1.0.patch") + XCTAssertEqual(deltaItem!.versionString, "3.0") // Test latest delta update item available var latestDeltaItem: SUAppcastItem? diff --git a/generate_appcast/FeedXML.swift b/generate_appcast/FeedXML.swift index f84f12a345..8159fa4767 100644 --- a/generate_appcast/FeedXML.swift +++ b/generate_appcast/FeedXML.swift @@ -71,7 +71,13 @@ func writeAppcast(appcastDestPath: URL, updates: [ArchiveItem]) throws { var numItems = 0 for update in updates { var item: XMLElement - let existingItems = try channel.nodes(forXPath: "item[enclosure[@sparkle:version=\"\(update.version)\"]]") + + var existingItems = try channel.nodes(forXPath: "item[enclosure[@\(SUAppcastAttributeVersion)=\"\(update.version)\"]]") + if existingItems.count == 0 { + // Fall back to see if any items are using the element version variant + existingItems = try channel.nodes(forXPath: "item[\(SUAppcastElementVersion)=\"\(update.version)\"]") + } + let createNewItem = existingItems.count == 0 // Update all old items, but aim for less than 5 in new feeds @@ -93,6 +99,20 @@ func writeAppcast(appcastDestPath: URL, updates: [ArchiveItem]) throws { if nil == findElement(name: "pubDate", parent: item) { item.addChild(XMLElement.element(withName: "pubDate", stringValue: update.pubDate) as! XMLElement) } + + var versionElement = findElement(name: SUAppcastElementVersion, parent: item) + if nil == versionElement { + versionElement = XMLElement.element(withName: SUAppcastElementVersion, uri: sparkleNS) as? XMLElement + item.addChild(versionElement!) + } + versionElement?.setChildren([text(update.version)]) + + var shortVersionElement = findElement(name: SUAppcastElementShortVersionString, parent: item) + if nil == shortVersionElement { + shortVersionElement = XMLElement.element(withName: SUAppcastElementShortVersionString, uri: sparkleNS) as? XMLElement + item.addChild(shortVersionElement!) + } + shortVersionElement?.setChildren([text(update.shortVersion)]) if let html = update.releaseNotesHTML { let descElement = findOrCreateElement(name: "description", parent: item) @@ -157,8 +177,6 @@ func writeAppcast(appcastDestPath: URL, updates: [ArchiveItem]) throws { } var attributes = [ XMLNode.attribute(withName: "url", stringValue: archiveURL) as! XMLNode, - XMLNode.attribute(withName: SUAppcastAttributeVersion, uri: sparkleNS, stringValue: update.version) as! XMLNode, - XMLNode.attribute(withName: SUAppcastAttributeShortVersionString, uri: sparkleNS, stringValue: update.shortVersion) as! XMLNode, XMLNode.attribute(withName: "length", stringValue: String(update.fileSize)) as! XMLNode, XMLNode.attribute(withName: "type", stringValue: update.mimeType) as! XMLNode, ] @@ -181,8 +199,6 @@ func writeAppcast(appcastDestPath: URL, updates: [ArchiveItem]) throws { for delta in update.deltas { var attributes = [ XMLNode.attribute(withName: "url", stringValue: URL(string: delta.archivePath.lastPathComponent.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed)!, relativeTo: update.archiveURL)!.absoluteString) as! XMLNode, - XMLNode.attribute(withName: SUAppcastAttributeVersion, uri: sparkleNS, stringValue: update.version) as! XMLNode, - XMLNode.attribute(withName: SUAppcastAttributeShortVersionString, uri: sparkleNS, stringValue: update.shortVersion) as! XMLNode, XMLNode.attribute(withName: SUAppcastAttributeDeltaFrom, uri: sparkleNS, stringValue: delta.fromVersion) as! XMLNode, XMLNode.attribute(withName: "length", stringValue: String(delta.fileSize)) as! XMLNode, XMLNode.attribute(withName: "type", stringValue: "application/octet-stream") as! XMLNode,