commit
60e6401b8b
|
@ -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 */,
|
||||
|
|
|
@ -7,5 +7,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
@IBOutlet var window: NSWindow!
|
||||
|
||||
func applicationDidFinishLaunching(_: Notification) {
|
||||
UpdateController().checkAndUpdateFromApp(sender: self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) ?? ""
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("/")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
10
Rakefile
10
Rakefile
|
@ -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 .')
|
||||
|
|
Loading…
Reference in New Issue