1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 03:50:08 +02:00

♻️ Reworked updater

This commit is contained in:
2023-02-05 17:58:44 +01:00
parent 78e682688b
commit 300880f3e5
10 changed files with 130 additions and 289 deletions

View File

@ -205,8 +205,6 @@
C46B2648298B324100084651 /* ReleaseManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46B2647298B324100084651 /* ReleaseManifest.swift */; };
C46B2649298B324100084651 /* ReleaseManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46B2647298B324100084651 /* ReleaseManifest.swift */; };
C46B2650298B3C2100084651 /* PHP Monitor Self-Updater.app in Resources */ = {isa = PBXBuildFile; fileRef = C46B264F298B3C2100084651 /* PHP Monitor Self-Updater.app */; };
C46E206D28299B3800D909D6 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */; };
C46EBC4428DB95F0007ACC74 /* ShellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */; };
C46EBC4528DB95F0007ACC74 /* ShellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */; };
@ -347,7 +345,6 @@
C471E84628F9BB650021E251 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; };
C471E84728F9BB650021E251 /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
C471E84828F9BB650021E251 /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; };
C471E84928F9BB650021E251 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
C471E84A28F9BB650021E251 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE736282ABA4F00A302C2 /* AppVersion.swift */; };
C471E84B28F9BB650021E251 /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; };
C471E84C28F9BB650021E251 /* EnvironmentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */; };
@ -437,7 +434,6 @@
C471E8A928F9BB8F0021E251 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; };
C471E8AA28F9BB8F0021E251 /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
C471E8AB28F9BB8F0021E251 /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; };
C471E8AC28F9BB8F0021E251 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
C471E8AD28F9BB8F0021E251 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE736282ABA4F00A302C2 /* AppVersion.swift */; };
C471E8AE28F9BB8F0021E251 /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; };
C471E8AF28F9BB8F0021E251 /* EnvironmentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */; };
@ -870,7 +866,6 @@
C469E702294CFDF700A82AB2 /* DomainsListTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainsListTest.swift; sourceTree = "<group>"; };
C46B2647298B324100084651 /* ReleaseManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseManifest.swift; sourceTree = "<group>"; };
C46B264F298B3C2100084651 /* PHP Monitor Self-Updater.app */ = {isa = PBXFileReference; lastKnownFileType = wrapper.application; path = "PHP Monitor Self-Updater.app"; sourceTree = "<group>"; };
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateChecker.swift; sourceTree = "<group>"; };
C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUpdaterCheckTest.swift; sourceTree = "<group>"; };
C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellProtocol.swift; sourceTree = "<group>"; };
C46EBC4628DB9644007ACC74 /* RealShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealShell.swift; sourceTree = "<group>"; };
@ -1557,7 +1552,6 @@
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
C4D8016522B1584700C6DA1B /* Startup.swift */,
C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */,
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */,
C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */,
C409349C298EE8E900D25014 /* AppUpdater.swift */,
@ -2198,7 +2192,6 @@
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */,
C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
C46E206D28299B3800D909D6 /* AppUpdateChecker.swift in Sources */,
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
03E36FE728D9219000636F7F /* ActiveShell.swift in Sources */,
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
@ -2302,7 +2295,6 @@
C471E84628F9BB650021E251 /* InterAppHandler.swift in Sources */,
C471E84728F9BB650021E251 /* Startup.swift in Sources */,
C471E84828F9BB650021E251 /* EnvironmentCheck.swift in Sources */,
C471E84928F9BB650021E251 /* AppUpdateChecker.swift in Sources */,
C471E84A28F9BB650021E251 /* AppVersion.swift in Sources */,
C471E84B28F9BB650021E251 /* ServicesManager.swift in Sources */,
C471E84C28F9BB650021E251 /* EnvironmentManager.swift in Sources */,
@ -2472,7 +2464,6 @@
C471E8A928F9BB8F0021E251 /* InterAppHandler.swift in Sources */,
C471E8AA28F9BB8F0021E251 /* Startup.swift in Sources */,
C471E8AB28F9BB8F0021E251 /* EnvironmentCheck.swift in Sources */,
C471E8AC28F9BB8F0021E251 /* AppUpdateChecker.swift in Sources */,
C471E8AD28F9BB8F0021E251 /* AppVersion.swift in Sources */,
C471E8AE28F9BB8F0021E251 /* ServicesManager.swift in Sources */,
C471E8AF28F9BB8F0021E251 /* EnvironmentManager.swift in Sources */,
@ -2795,7 +2786,6 @@
C46EBC4B28DB966A007ACC74 /* TestableShell.swift in Sources */,
C40FE73B282ABB2E00A302C2 /* AppVersionTest.swift in Sources */,
C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */,
C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -1,228 +0,0 @@
//
// Updater.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 09/05/2022.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Foundation
import AppKit
class AppUpdateChecker {
public static var latestCaskFileContents: String = ""
public static var enabled: Bool = {
return Preferences.isEnabled(.automaticBackgroundUpdateCheck)
}()
public static var isDev: Bool = {
return App.version.contains("-dev")
}()
public static func retrieveVersionFromCask(
_ initiatedFromBackground: Bool = true
) async -> String {
let caskFile = App.version.contains("-dev")
? Constants.Urls.DevBuildCaskFile.absoluteString
: Constants.Urls.StableBuildCaskFile.absoluteString
var command = "curl -s"
if initiatedFromBackground {
command = "curl -s --max-time 5"
}
AppUpdateChecker.latestCaskFileContents = await Shell.pipe("\(command) '\(caskFile)'").out
return await Shell.pipe("echo \"\(Self.latestCaskFileContents)\" | grep version").out
}
public static func checkIfNewerVersionIsAvailable(
initiatedFromBackground: Bool = true
) async {
if initiatedFromBackground {
if !Preferences.isEnabled(.automaticBackgroundUpdateCheck) {
Log.info("Automatic updates are disabled. No check will be performed.")
return
}
Log.info("Automatic updates are enabled, a check will be performed.")
}
let versionString = await retrieveVersionFromCask(initiatedFromBackground)
guard let onlineVersion = AppVersion.from(versionString) else {
Log.err("We couldn't check for updates!")
// Only notify about connection issues if the request to check for updates was explicit
if !initiatedFromBackground {
notifyAboutConnectionIssue()
}
return
}
let currentVersion = AppVersion.fromCurrentVersion()
handleVersionComparison(
currentVersion,
onlineVersion,
initiatedFromBackground
)
}
private static func handleVersionComparison(
_ currentVersion: AppVersion,
_ onlineVersion: AppVersion,
_ background: Bool
) {
// TODO: Restore original behaviour
notifyAboutNewerVersion(version: onlineVersion)
return
switch onlineVersion.version.versionCompare(currentVersion.version) {
case .orderedAscending:
Log.info("You are running a newer version of PHP Monitor "
+ "(\(currentVersion.computerReadable) > \(onlineVersion.computerReadable)).")
if !background { notifyVersionDoesNotNeedUpgrade() }
case .orderedDescending:
Log.info("There is a newer version (\(onlineVersion)) available! "
+ "(\(onlineVersion.computerReadable) > \(currentVersion.computerReadable))")
notifyAboutNewerVersion(version: onlineVersion)
case .orderedSame:
if currentVersion.build != nil
&& onlineVersion.build != nil
&& buildDiffers(currentVersion, onlineVersion, background) {
return
}
Log.info("The installed version (\(currentVersion.computerReadable)) matches the latest release "
+ "(\(onlineVersion.computerReadable)).")
if !background { notifyVersionDoesNotNeedUpgrade() }
}
}
private static func buildDiffers(
_ currentVersion: AppVersion,
_ onlineVersion: AppVersion,
_ background: Bool
) -> Bool {
if onlineVersion.build! > currentVersion.build! {
Log.info("There is a newer build of PHP Monitor available! "
+ "(\(onlineVersion.computerReadable) > \(currentVersion.computerReadable))")
notifyAboutNewerVersion(version: onlineVersion)
return true
} else if onlineVersion.build! < currentVersion.build! {
Log.info("You are running a newer build of PHP Monitor "
+ "(\(currentVersion.computerReadable) > \(onlineVersion.computerReadable)).")
if !background { notifyVersionDoesNotNeedUpgrade() }
return true
}
return false
}
private static func notifyVersionDoesNotNeedUpgrade() {
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.is_latest_version.title".localized,
subtitle: "updater.alerts.is_latest_version.subtitle".localized(App.shortVersion),
description: ""
)
.withPrimary(text: "generic.ok".localized)
.show()
}
}
private static func notifyAboutNewerVersion(version: AppVersion) {
let devSuffix = isDev ? "-dev" : ""
let command = isDev ? "brew upgrade phpmon-dev" : "brew upgrade phpmon"
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.newer_version_available.title".localized(version.humanReadable),
subtitle: "updater.alerts.newer_version_available.subtitle".localized,
description: HomebrewDiagnostics.customCaskInstalled
? "updater.installation_source.brew".localized(command)
: "updater.installation_source.direct".localized
)
.withPrimary(
text: "updater.alerts.buttons.install".localized,
action: { vc in
Self.installUpdate()
vc.close(with: .OK)
}
)
.withSecondary(
text: "updater.alerts.buttons.release_notes".localized,
action: { vc in
vc.close(with: .OK)
NSWorkspace.shared.open(
Constants.Urls.GitHubReleases.appendingPathComponent("/tag/v\(version.tagged)\(devSuffix)")
)
}
)
.withTertiary(text: "Dismiss", action: { vc in
vc.close(with: .OK)
})
.show()
}
}
private static func installUpdate() {
let updater = Bundle.main.resourceURL!.path + "/PHP Monitor Self-Updater.app"
system_quiet("mkdir -p ~/.config/phpmon/updater 2> /dev/null")
let updaterDirectory = "~/.config/phpmon/updater"
.replacingOccurrences(of: "~", with: NSHomeDirectory())
system_quiet("cp -R \"\(updater)\" \"\(updaterDirectory)/PHP Monitor Self-Updater.app\"")
let sha256 = system("echo \"\(Self.latestCaskFileContents)\" | grep sha256")
.trimmingCharacters(in: .whitespacesAndNewlines)
.replacingOccurrences(of: "'", with: "")
.split(separator: " ").last ?? ""
let url = system("echo \"\(Self.latestCaskFileContents)\" | grep url")
.trimmingCharacters(in: .whitespacesAndNewlines)
.replacingOccurrences(of: "'", with: "")
.split(separator: " ").last ?? ""
try! FileSystem.writeAtomicallyToFile(
"\(updaterDirectory)/update.json",
content: """
{ "url": "\(url)", "sha256": "\(sha256)" }
"""
)
let updaterUrl = NSURL(fileURLWithPath: updater, isDirectory: true) as URL
let configuration = NSWorkspace.OpenConfiguration()
NSWorkspace.shared.openApplication(at: updaterUrl, configuration: configuration) { _, _ in
print("The updater has been launched successfully!")
}
}
private static func notifyAboutConnectionIssue() {
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.cannot_check_for_update.title".localized,
subtitle: "updater.alerts.cannot_check_for_update.subtitle".localized,
description: "updater.alerts.cannot_check_for_update.description".localized(
App.version
)
)
.withTertiary(
text: "updater.alerts.buttons.releases_on_github".localized,
action: { _ in
NSWorkspace.shared.open(Constants.Urls.GitHubReleases)
}
)
.withPrimary(text: "generic.ok".localized)
.show()
}
}
}

View File

@ -7,75 +7,160 @@
//
import Foundation
import Cocoa
class AppUpdater {
var caskFile: CaskFile!
var latestVersionOnline: AppVersion!
var interactive: Bool = false
public func checkForUpdates(background: Bool) async {
if background && !Preferences.isEnabled(.automaticBackgroundUpdateCheck) {
public func checkForUpdates(interactive: Bool) async {
self.interactive = interactive
if interactive && !Preferences.isEnabled(.automaticBackgroundUpdateCheck) {
Log.info("Skipping automatic update check due to user preference.")
return
}
Log.info("The app will search for updates...")
let caskUrl = App.version.contains("-dev")
let caskUrl = App.identifier.contains(".dev")
? Constants.Urls.DevBuildCaskFile
: Constants.Urls.StableBuildCaskFile
guard let caskFile = await CaskFile.from(url: caskUrl) else {
Log.err("The contents of the CaskFile at '\(caskUrl.absoluteString)' could not be retrieved.")
if !background {
return presentCouldNotRetrieveUpdate()
} else {
return
}
return presentCouldNotRetrieveUpdateIfInteractive()
}
self.caskFile = caskFile
if newerVersionExists() {
presentNewerVersionAvailableAlert()
} else {
if !background {
presentNoNewerVersionAvailableAlert()
}
}
}
var caskFile: CaskFile!
public func newerVersionExists() -> Bool {
let currentVersion = AppVersion.fromCurrentVersion()
guard let onlineVersion = AppVersion.from(caskFile.version) else {
Log.err("The version string from the CaskFile could not be read.")
return false
return presentCouldNotRetrieveUpdateIfInteractive()
}
Log.info("You are running \(currentVersion.computerReadable). The latest version is: \(onlineVersion.computerReadable).")
latestVersionOnline = onlineVersion
Log.info("The latest version read from '\(caskUrl.lastPathComponent)' is: v\(onlineVersion.computerReadable).")
// Do the comparison w/ current version
return true
if latestVersionOnline > currentVersion {
presentNewerVersionAvailableAlert()
} else if interactive {
presentNoNewerVersionAvailableAlert()
}
}
private func presentCouldNotRetrieveUpdateIfInteractive() {
if interactive {
return presentCouldNotRetrieveUpdate()
} else {
return
}
}
// MARK: - Alerts
public func presentNewerVersionAvailableAlert() {
print("A newer version is available")
let command = App.identifier.contains(".dev")
? "brew upgrade phpmon-dev"
: "brew upgrade phpmon"
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.newer_version_available.title"
.localized(latestVersionOnline.humanReadable),
subtitle: "updater.alerts.newer_version_available.subtitle"
.localized,
description: HomebrewDiagnostics.customCaskInstalled
? "updater.installation_source.brew".localized(command)
: "updater.installation_source.direct".localized
)
.withPrimary(
text: "updater.alerts.buttons.install".localized,
action: { vc in
self.prepareForDownload()
vc.close(with: .OK)
}
)
.withSecondary(
text: "updater.alerts.buttons.release_notes".localized,
action: { _ in
let urlSegments = self.caskFile.url.split(separator: "/")
let tag = urlSegments[urlSegments.count - 2] // ../download/{tag}/{file.zip}
NSWorkspace.shared.open(
Constants.Urls.GitHubReleases.appendingPathComponent("/tag/\(tag)")
)
}
)
.withTertiary(text: "updater.alerts.buttons.dismiss".localized, action: { vc in
vc.close(with: .OK)
})
.show()
}
}
public func presentNoNewerVersionAvailableAlert() {
print("No newer version is available")
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.is_latest_version.title".localized,
subtitle: "updater.alerts.is_latest_version.subtitle".localized(App.shortVersion),
description: ""
)
.withPrimary(text: "generic.ok".localized)
.show()
}
}
public func presentCouldNotRetrieveUpdate() {
print("Could not retrieve update")
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.cannot_check_for_update.title".localized,
subtitle: "updater.alerts.cannot_check_for_update.subtitle".localized,
description: "updater.alerts.cannot_check_for_update.description".localized(
App.version
)
)
.withTertiary(
text: "updater.alerts.buttons.releases_on_github".localized,
action: { _ in
NSWorkspace.shared.open(Constants.Urls.GitHubReleases)
}
)
.withPrimary(text: "generic.ok".localized)
.show()
}
}
// MARK: - Preparing for Self-Updater
private func prepareForDownload() {
let updater = Bundle.main.resourceURL!.path + "/PHP Monitor Self-Updater.app"
system_quiet("mkdir -p ~/.config/phpmon/updater 2> /dev/null")
let updaterDirectory = "~/.config/phpmon/updater"
.replacingOccurrences(of: "~", with: NSHomeDirectory())
system_quiet("cp -R \"\(updater)\" \"\(updaterDirectory)/PHP Monitor Self-Updater.app\"")
try! FileSystem.writeAtomicallyToFile(
"\(updaterDirectory)/update.json",
content: "{ \"url\": \"\(caskFile.url)\", \"sha256\": \"\(caskFile.sha256)\" }"
)
let updaterUrl = NSURL(fileURLWithPath: updater, isDirectory: true) as URL
let configuration = NSWorkspace.OpenConfiguration()
NSWorkspace.shared.openApplication(at: updaterUrl, configuration: configuration) { _, _ in
Log.info("The updater has been launched successfully!")
}
}
public static func checkIfUpgradeWasPerformed() {
// MARK: - Checking if Self-Updater Worked
public static func checkIfUpdateWasPerformed() {
if FileSystem.fileExists("~/.config/phpmon/updater/upgrade.success") {
// Send a notification about the update
Task { @MainActor in

View File

@ -87,7 +87,7 @@ class AppVersion: Comparable {
static func < (lhs: AppVersion, rhs: AppVersion) -> Bool {
let comparisonResult = lhs.version.versionCompare(rhs.version)
if comparisonResult == .orderedDescending {
if comparisonResult == .orderedAscending {
return true
}

View File

@ -75,5 +75,4 @@ struct CaskFile {
return CaskFile(properties: props)
}
}

View File

@ -112,10 +112,7 @@ extension MainMenu {
}
}
// await AppUpdateChecker.checkIfNewerVersionIsAvailable()
await AppUpdater().checkForUpdates(background: true)
exit(0)
await AppUpdater().checkForUpdates(interactive: false)
}
// Check if the linked version has changed between launches of phpmon
@ -125,7 +122,7 @@ extension MainMenu {
Log.info("PHP Monitor is ready to serve!")
// Check if we upgraded just now
AppUpdater.checkIfUpgradeWasPerformed()
AppUpdater.checkIfUpdateWasPerformed()
}
/**

View File

@ -199,7 +199,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
}
@objc func checkForUpdates() {
Task { await AppUpdateChecker.checkIfNewerVersionIsAvailable(initiatedFromBackground: false) }
Task { await AppUpdater().checkForUpdates(interactive: true) }
}
// MARK: - Menu Delegate

View File

@ -623,8 +623,8 @@ COMMON TROUBLESHOOTING TIPS
"updater.alerts.newer_version_available.title" = "PHP Monitor v%@ is now available!";
"updater.alerts.newer_version_available.subtitle" = "Keeping PHP Monitor up-to-date is highly recommended, since newer versions usually fix bugs and include fixes to support the latest versions of Valet and PHP.";
"updater.installation_source.brew" = "The recommended method of installing updates to PHP Monitor is to simply press 'Install Update'. This will launch the separate PHP Monitor Self-Updater, which will update the app. You may also upgrade via the terminal by running `%@`.";
"updater.installation_source.direct" = "The recommended method of installing updates to PHP Monitor is to simply press 'Install Update'. This will launch the separate PHP Monitor Self-Updater, which will update the app.";
"updater.installation_source.brew" = "The recommended method of installing updates to PHP Monitor is to simply press 'Install Update'.\n\n(You may also upgrade via the terminal by running `%@`, but this is not recommended.)";
"updater.installation_source.direct" = "The recommended method of installing updates to PHP Monitor is to simply press 'Install Update'.";
"updater.alerts.buttons.release_notes" = "View Release Notes";
"updater.alerts.is_latest_version.title" = "PHP Monitor is up-to-date!";
@ -635,6 +635,7 @@ COMMON TROUBLESHOOTING TIPS
"updater.alerts.cannot_check_for_update.description" = "The currently installed version is: %@. You can go to the list of the latest releases (on GitHub) by clicking on the button on the left.";
"updater.alerts.buttons.releases_on_github" = "View Releases";
"updater.alerts.buttons.install" = "Install Update";
"updater.alerts.buttons.dismiss" = "Dismiss";
// WARNINGS ABOUT NON-DEFAULT TLD

View File

@ -49,4 +49,5 @@ class CaskFileParserTest: XCTestCase {
XCTAssertTrue(caskFile.properties.keys.contains("url"))
XCTAssertTrue(caskFile.properties.keys.contains("appcast"))
}
}

View File

@ -60,21 +60,17 @@ class AppVersionTest: XCTestCase {
}
func test_can_compare_version_numbers() {
var first = AppVersion.from("5.0_100")!
var second = AppVersion.from("5.0_101")!
XCTAssertTrue(second > first)
// Build is newer
XCTAssertTrue(AppVersion.from("5.0_101")! > AppVersion.from("5.0_100")!)
first = AppVersion.from("5.0_100")!
second = AppVersion.from("5.0_100")!
XCTAssertFalse(second > first)
// Version and build is the same
XCTAssertFalse(AppVersion.from("5.0.0_100")! > AppVersion.from("5.0_100")!)
first = AppVersion.from("5.0_100")!
second = AppVersion.from("5.0.1_100")!
XCTAssertFalse(second > first)
// Version is newer
XCTAssertTrue(AppVersion.from("5.1_100")! > AppVersion.from("5.0_100")!)
first = AppVersion.from("5.0_102")!
second = AppVersion.from("5.0_101")!
XCTAssertFalse(second > first)
// Build is older
XCTAssertFalse(AppVersion.from("5.0_101")! > AppVersion.from("5.0_102")!)
}
}