Merge pull request #26 from xcbuddy/fix-update

Fix auto-updates
This commit is contained in:
Pedro Piñera Buendía 2018-05-09 00:02:39 +02:00 committed by GitHub
commit 60e6401b8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 104 additions and 85 deletions

View File

@ -55,8 +55,6 @@
B9B629A920864E3A00EE9E07 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B629A220864E3A00EE9E07 /* Logger.swift */; };
B9B629AB20864E3A00EE9E07 /* Printer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B629A420864E3A00EE9E07 /* Printer.swift */; };
B9B629AC20864E3A00EE9E07 /* FileHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B629A520864E3A00EE9E07 /* FileHandler.swift */; };
B9B629AD20864E3A00EE9E07 /* Bundles.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B629A620864E3A00EE9E07 /* Bundles.swift */; };
B9B629AE20864E3A00EE9E07 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B629A720864E3A00EE9E07 /* App.swift */; };
B9B629AF20864E3A00EE9E07 /* Signals.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B629A820864E3A00EE9E07 /* Signals.swift */; };
B9B629B420864E4700EE9E07 /* SPUUpdater+CommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B629B120864E4700EE9E07 /* SPUUpdater+CommandLine.swift */; };
B9B629B520864E4700EE9E07 /* UpdateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B629B220864E4700EE9E07 /* UpdateController.swift */; };
@ -282,8 +280,6 @@
B9B629A220864E3A00EE9E07 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
B9B629A420864E3A00EE9E07 /* Printer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Printer.swift; sourceTree = "<group>"; };
B9B629A520864E3A00EE9E07 /* FileHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileHandler.swift; sourceTree = "<group>"; };
B9B629A620864E3A00EE9E07 /* Bundles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bundles.swift; sourceTree = "<group>"; };
B9B629A720864E3A00EE9E07 /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
B9B629A820864E3A00EE9E07 /* Signals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signals.swift; sourceTree = "<group>"; };
B9B629B120864E4700EE9E07 /* SPUUpdater+CommandLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SPUUpdater+CommandLine.swift"; sourceTree = "<group>"; };
B9B629B220864E4700EE9E07 /* UpdateController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateController.swift; sourceTree = "<group>"; };
@ -604,8 +600,6 @@
B9B629A220864E3A00EE9E07 /* Logger.swift */,
B9B629A420864E3A00EE9E07 /* Printer.swift */,
B9B629A520864E3A00EE9E07 /* FileHandler.swift */,
B9B629A620864E3A00EE9E07 /* Bundles.swift */,
B9B629A720864E3A00EE9E07 /* App.swift */,
B9B629A820864E3A00EE9E07 /* Signals.swift */,
B9553DE92090690000050311 /* Shell.swift */,
B9553DF02092181500050311 /* Context.swift */,
@ -991,7 +985,6 @@
B9553DF5209228EB00050311 /* MockRersourceLocator.swift in Sources */,
B9B629B520864E4700EE9E07 /* UpdateController.swift in Sources */,
B9FB2DE2208653F900BC2FB3 /* GraphLoader.swift in Sources */,
B9B629AD20864E3A00EE9E07 /* Bundles.swift in Sources */,
B9FB2DDC2086539A00BC2FB3 /* GraphLoadingError.swift in Sources */,
B9FB2DE0208653EF00BC2FB3 /* GraphManifestLoader.swift in Sources */,
B9E2DCA820876F8A0061DF86 /* Command.swift in Sources */,
@ -1010,7 +1003,6 @@
B9553DF320921AF300050311 /* ResourceLocator.swift in Sources */,
B9B629A920864E3A00EE9E07 /* Logger.swift in Sources */,
B9FB2DE72086544900BC2FB3 /* Project.swift in Sources */,
B9B629AE20864E3A00EE9E07 /* App.swift in Sources */,
B9FB2DE62086544900BC2FB3 /* Scheme.swift in Sources */,
B9F1EDFD208D26B200477835 /* GraphValidator.swift in Sources */,
B95895FB208A2FFB00F00ACF /* WorkspaceGenerator.swift in Sources */,

View File

@ -7,5 +7,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet var window: NSWindow!
func applicationDidFinishLaunching(_: Notification) {
UpdateController().checkAndUpdateFromApp(sender: self)
}
}

View File

@ -27,7 +27,7 @@ public class UpdateCommand: NSObject, Command, SPUUpdaterDelegate {
public func run(with _: ArgumentParser.Result) {
context.errorHandler.try {
try controller.checkAndUpdateFromConsole()
try controller.checkAndUpdateFromConsole(context: self.context)
}
}

View File

@ -1,9 +1,24 @@
import Foundation
import Sparkle
/// Command line user driver for updates.
class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
// MARK: Attributes
/// Sparke user driver core component.
let coreComponent = SPUUserDriverCoreComponent()
/// Context
let context: Contexting
/// Initializes the command line user drive.
///
/// - Parameter context: context.
init(context: Contexting) {
self.context = context
}
// MARK: - SPUUserDriver
func showCanCheck(forUpdates canCheckForUpdates: Bool) {
@ -22,7 +37,7 @@ class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
func showUserInitiatedUpdateCheck(completion updateCheckStatusCompletion: @escaping (SPUUserInitiatedCheckStatus) -> Void) {
DispatchQueue.main.async {
self.coreComponent.registerUpdateCheckStatusHandler(updateCheckStatusCompletion)
print("Checking for updates...")
self.context.printer.print("Checking for updates...")
}
}
@ -58,7 +73,7 @@ class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
userInitiated _: Bool,
reply: @escaping (SPUInformationalUpdateAlertChoice) -> Void) {
DispatchQueue.main.async {
print("Found information for new update: %s", appcastItem.infoURL.absoluteString.utf8)
self.context.printer.print("Found information for new update: \(appcastItem.infoURL.absoluteString.utf8)")
reply(.dismissInformationalNoticeChoice)
}
}
@ -77,13 +92,13 @@ class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
func showUpdateReleaseNotesFailedToDownloadWithError(_ error: Error) {
DispatchQueue.main.async {
print("Error: Unable to download release notes: %s", error.localizedDescription.utf8)
self.context.printer.print(errorMessage: "Error: Unable to download release notes: \(error.localizedDescription.utf8)")
}
}
func showUpdateNotFound(acknowledgement _: @escaping () -> Void) {
DispatchQueue.main.async {
print("No new update available!")
self.context.printer.print("No new update available!")
exit(EXIT_SUCCESS)
}
}
@ -98,7 +113,7 @@ class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
func showDownloadInitiated(completion downloadUpdateStatusCompletion: @escaping (SPUDownloadUpdateStatus) -> Void) {
DispatchQueue.main.async {
self.coreComponent.registerDownloadStatusHandler(downloadUpdateStatusCompletion)
print("Downloading Update...")
self.context.printer.print("Downloading Update...")
}
}
@ -113,13 +128,13 @@ class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
func showDownloadDidStartExtractingUpdate() {
DispatchQueue.main.async {
self.coreComponent.completeDownloadStatus()
print("Extracting update...")
self.context.printer.print("Extracting update...")
}
}
func showExtractionReceivedProgress(_ progress: Double) {
func showExtractionReceivedProgress(_: Double) {
DispatchQueue.main.async {
print("Extracting Update (%.0f%%)", progress * 100)
// self.context.printer.print(String("Extracting Update (%.0f%%)", progress * 100))
}
}
@ -132,7 +147,7 @@ class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
func showInstallingUpdate() {
DispatchQueue.main.async {
print("Installing update...")
self.context.printer.print("Installing update...")
}
}
@ -143,14 +158,14 @@ class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
func showUpdateInstallationDidFinish(acknowledgement: @escaping () -> Void) {
DispatchQueue.main.async {
self.coreComponent.registerAcknowledgement(acknowledgement)
print("Installation finished.")
self.context.printer.print("Installation finished.")
self.coreComponent.acceptAcknowledgement()
}
}
func dismissUpdateInstallation() {
DispatchQueue.main.async {
print("Exiting.")
self.context.printer.print("Exiting.")
exit(0)
}
}
@ -158,7 +173,7 @@ class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
// MARK: - Private
private func showUpdate(appcastItem: SUAppcastItem, updateAdjective: String) {
print("Found (%s) update! (%s)", updateAdjective, appcastItem.displayVersionString.utf8)
context.printer.print("Found (\(updateAdjective) update! (\(appcastItem.displayVersionString.utf8))")
if appcastItem.itemDescription != nil {
let data = appcastItem.itemDescription.data(using: .utf8) ?? Data()
display(htmlReleaseNotes: data)
@ -171,8 +186,8 @@ class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
}
private func display(releaseNotes: Any) {
print("Release notes:")
print("%s", releaseNotes)
context.printer.print("Release notes:")
context.printer.print("\(releaseNotes)")
}
}
@ -181,10 +196,11 @@ class SPUCommandLineUserDriver: NSObject, SPUUserDriver {
private var _commandlineUpdater: SPUUpdater!
extension SPUUpdater {
static func commandLine() throws -> SPUUpdater {
static func commandLine(context: Contexting,
resourceLocator: ResourceLocating = ResourceLocator()) throws -> SPUUpdater {
if _commandlineUpdater != nil { return _commandlineUpdater }
let driver = SPUCommandLineUserDriver()
let bundle = try Bundle.app()
let driver = SPUCommandLineUserDriver(context: context)
let bundle = try Bundle(path: resourceLocator.appPath().asString)!
_commandlineUpdater = SPUUpdater(hostBundle: bundle, applicationBundle: bundle, userDriver: driver, delegate: nil)
return _commandlineUpdater
}

View File

@ -1,22 +1,47 @@
import Foundation
import Sparkle
/// Defines the interface of a controller that handles app updates.
protocol UpdateControlling: AnyObject {
func checkAndUpdateFromConsole() throws
/// Checks and update from the console.
/// Intended to be used from the CLI.
///
/// - Parameter context: context.
/// - Throws: an error if the check and update process fails.
func checkAndUpdateFromConsole(context: Contexting) throws
/// Checks and updates from the app.
///
/// - Parameter sender: sender.
func checkAndUpdateFromApp(sender: Any)
}
class UpdateController: UpdateControlling {
init() {}
/// Default update controller.
public class UpdateController: UpdateControlling {
/// Constructor.
public init() {}
func checkAndUpdateFromConsole() throws {
let updater = SPUUpdater.commandLine
try updater().checkForUpdates()
try updater().start()
/// Checks and update from the console.
/// Intended to be used from the CLI.
///
/// - Throws: an error if the check and update process fails.
/// Checks and update from the console.
/// Intended to be used from the CLI.
///
/// - Parameter context: context.
/// - Throws: an error if the check and update process fails.
func checkAndUpdateFromConsole(context: Contexting) throws {
let updater = try SPUUpdater.commandLine(context: context)
updater.checkForUpdates()
try updater.start()
RunLoop.current.run()
}
func checkAndUpdateFromApp(sender: Any) {
/// Checks and updates from the app.
///
/// - Parameter sender: sender.
public func checkAndUpdateFromApp(sender: Any) {
let updater = SPUStandardUpdaterController.app
updater.checkForUpdates(sender)
}

View File

@ -1,24 +0,0 @@
import Foundation
/// Util class that contains information about the app.
public class App {
/// App's bundle information dictionary.
private let infoDictionary: [String: Any]
/// Default constructor.
public convenience init() throws {
try self.init(infoDictionary: Bundle.app().infoDictionary!)
}
/// Initializes the app with the app's bundle info dictionary.
///
/// - Parameter infoDictionary: info dictionary.
public init(infoDictionary: [String: Any]) {
self.infoDictionary = infoDictionary
}
/// App version.
public var version: String {
return (infoDictionary["CFBundleShortVersionString"] as? String) ?? ""
}
}

View File

@ -1,22 +0,0 @@
import Basic
import Foundation
private class Dummy {}
enum BundleError: Error {
case frameworkNotEmbedded(AbsolutePath)
}
// MARK: - Bundle Extension
extension Bundle {
/// If xcbuddykit is embedded in the an app, this getter returns the bundle of the app where the framework is contained.
static func app() throws -> Bundle {
let path = Bundle(for: Dummy.self).bundleURL
let appBundlePath = path.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent()
if appBundlePath.pathExtension == "app" {
return Bundle(url: appBundlePath)!
}
throw BundleError.frameworkNotEmbedded(AbsolutePath(path.path))
}
}

View File

@ -16,6 +16,12 @@ protocol ResourceLocating: AnyObject {
/// - Returns: path to the xcbuddy CLI.
/// - Throws: an error if the CLI cannot be found.
func cliPath() throws -> AbsolutePath
/// Returns the app bundle.
///
/// - Returns: app bundle
/// - Throws: an error if the bundle cannot be found.
func appPath() throws -> AbsolutePath
}
/// Resource locating error.
@ -105,4 +111,18 @@ final class ResourceLocator: ResourceLocating {
}
return toolPath
}
/// Returns the app bundle.
///
/// - Returns: app bundle
/// - Throws: an error if the bundle cannot be found.
func appPath() throws -> AbsolutePath {
let path = AbsolutePath(Bundle(for: ResourceLocator.self).bundleURL.path)
let appPath = path.parentDirectory.parentDirectory.parentDirectory
if appPath.extension == "app" {
return appPath
} else {
throw ResourceLocatingError.notFound("xcbuddy.app")
}
}
}

View File

@ -6,6 +6,8 @@ final class MockResourceLocator: ResourceLocating {
var projectDescriptionStub: (() throws -> AbsolutePath)?
var cliPathCount: UInt = 0
var cliPathStub: (() throws -> AbsolutePath)?
var appPathCount: UInt = 0
var appPathStub: (() throws -> AbsolutePath)?
func projectDescription() throws -> AbsolutePath {
projectDescriptionCount += 1
@ -16,4 +18,9 @@ final class MockResourceLocator: ResourceLocating {
cliPathCount += 1
return try cliPathStub?() ?? AbsolutePath("/")
}
func appPath() throws -> AbsolutePath {
appPathCount += 1
return try appPathStub?() ?? AbsolutePath("/")
}
}

View File

@ -12,6 +12,10 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
* Error handling https://github.com/xcbuddy/xcbuddy/pull/14 by @pepibumur.
### Fixed
* Auto updates https://github.com/xcbuddy/xcbuddy/pull/26 by @pepibumur.
## 0.33.0
**Release Date:** 27/04/2018

View File

@ -59,10 +59,10 @@ def bump_version
puts("Bumping version to #{new_version}(#{new_bundle_version})")
execute("/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion #{new_bundle_version}\" \"#{info_plist_path}\"")
execute("/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString #{new_version}\" \"#{info_plist_path}\"")
[current_version, new_version]
[new_bundle_version, new_version]
end
def add_appcast_entry(version, length, signature)
def add_appcast_entry(version, new_bundle_version, length, signature)
appcast_string = File.open(APPCAST_PATH, 'rb', &:read)
item = Sparklecast::Appcast::Item.new
item.title = "Version #{version}"
@ -70,7 +70,7 @@ def add_appcast_entry(version, length, signature)
item.length = length
item.dsa_signature = signature
item.pub_date = Time.now
item.sparkle_version = version
item.sparkle_version = new_bundle_version
appcast_string = Sparklecast::Appcast.add_item(appcast_string, item)
File.open(APPCAST_PATH, 'w') { |file| file.write(appcast_string) }
end
@ -116,7 +116,7 @@ def release
end
# Bump version
current_version, new_version = bump_version
new_bundle_version, new_version = bump_version
# Archiving
archive_and_export
@ -125,7 +125,7 @@ def release
# Updating appcast.xml
signature = `./bin/sign_update #{BUILD_PATH}/#{APP_NAME}.zip keys/dsa_priv.pem`.delete("\n")
length = `stat -f%z #{BUILD_PATH}/#{APP_NAME}.zip`.strip
add_appcast_entry(new_version, length, signature)
add_appcast_entry(new_version, new_bundle_version, length, signature)
# Commiting changes
execute('git add .')