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

🏗 WIP: Check for updates

This commit is contained in:
2022-05-09 23:41:52 +02:00
parent b82811e6bf
commit f413b84a45
9 changed files with 199 additions and 29 deletions

View File

@ -117,6 +117,8 @@
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */; };
C46E206D28299B3800D909D6 /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* Updater.swift */; };
C46E206E28299B3800D909D6 /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* Updater.swift */; };
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
C473319F2470923A009A0597 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; };
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
@ -337,6 +339,7 @@
C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListWC.swift; sourceTree = "<group>"; };
C464ADAE275A7A69003FCD53 /* DomainListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListVC.swift; sourceTree = "<group>"; };
C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListCellProtocol.swift; sourceTree = "<group>"; };
C46E206C28299B3800D909D6 /* Updater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Updater.swift; sourceTree = "<group>"; };
C46FA23E246C358E00944F05 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = "<group>"; };
C473319E2470923A009A0597 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
C47331A1247093B7009A0597 /* StatusMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMenu.swift; sourceTree = "<group>"; };
@ -786,6 +789,7 @@
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
C4D8016522B1584700C6DA1B /* Startup.swift */,
C46E206C28299B3800D909D6 /* Updater.swift */,
);
path = App;
sourceTree = "<group>";
@ -1178,6 +1182,7 @@
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */,
C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
C46E206D28299B3800D909D6 /* Updater.swift in Sources */,
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
@ -1346,6 +1351,7 @@
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */,
C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */,
C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */,
C46E206E28299B3800D909D6 /* Updater.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -65,6 +65,10 @@ struct Constants {
string: "https://github.com/nicoverbruggen/phpmon#%EF%B8%8F-faq--troubleshooting"
)!
static let GitHubReleases = URL(
string: "https://github.com/nicoverbruggen/phpmon/releases"
)!
static let StableBuildCaskFile = URL(
string: "https://raw.githubusercontent.com/nicoverbruggen/homebrew-cask/master/Casks/phpmon.rb"
)!

View File

@ -0,0 +1,120 @@
//
// Updater.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 09/05/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
import AppKit
class Updater {
public static var enabled: Bool = {
return Preferences.isEnabled(.automaticBackgroundUpdateCheck)
}()
public static func checkForUpdates(background: Bool = true) {
// Information about the status of a potential background update
if background {
if !Preferences.isEnabled(.automaticBackgroundUpdateCheck) {
Log.info("Automatic updates are disabled. No check will be performed.")
return
} else {
Log.info("Automatic updates are enabled, a check will be performed.")
}
}
// Actually check for updates
let caskFile = App.version.contains("-dev")
? Constants.Urls.DevBuildCaskFile.absoluteString
: Constants.Urls.StableBuildCaskFile.absoluteString
// We'll find out what the new version is by using `curl`
var command = "curl -s"
if background {
// If running as a background check, should only waste at most 2 secs of time
command = "curl -s --max-time 2"
}
let versionString = Shell.pipe(
"\(command) '\(caskFile)' | grep version"
)
guard let onlineVersion = VersionExtractor.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 !background {
notifyAboutConnectionIssue()
}
return
}
guard let current = VersionExtractor.from(App.shortVersion) else {
Log.err("We couldn't parse the current version number!")
return
}
switch onlineVersion.versionCompare(current) {
case .orderedAscending:
Log.info("You are running a newer version of PHP Monitor.")
case .orderedDescending:
Log.info("There is a newer version (\(onlineVersion)) available!")
notifyAboutNewerVersion(version: onlineVersion)
case .orderedSame:
Log.info("The installed version \(current) matches the latest release (\(onlineVersion)).")
}
}
private static func notifyAboutNewerVersion(version: String) {
let dev = App.version.contains("-dev") ? "-dev" : ""
DispatchQueue.main.async {
BetterAlert().withInformation(
title: "updater.alerts.newer_version_available.title".localized(version),
subtitle: "updater.alerts.newer_version_available.subtitle".localized,
description: HomebrewDiagnostics.customCaskInstalled
? "updater.installation_source.brew".localized
: "updater.installation_source.direct".localized
)
.withPrimary(
text: "updater.alerts.buttons.release_notes".localized,
action: { vc in
vc.close(with: .OK)
NSWorkspace.shared.open(
Constants.Urls.GitHubReleases.appendingPathComponent("/tag/v\(version)\(dev)")
)
}
)
.withTertiary(text: "Close", action: { vc in
vc.close(with: .OK)
})
.show()
}
}
private static func notifyAboutConnectionIssue() {
DispatchQueue.main.async {
BetterAlert().withInformation(
title: "updater.errors.cannot_check_for_update.title".localized,
subtitle: "updater.errors.cannot_check_for_update.subtitle".localized,
description: "updater.errors.cannot_check_for_update.description".localized(
App.version
)
)
.withTertiary(
text: "updater.errors.buttons.releases_on_github".localized,
action: { _ in
NSWorkspace.shared.open(Constants.Urls.GitHubReleases)
}
)
.withPrimary(text: "OK")
.show()
}
}
}

View File

@ -10,6 +10,25 @@ import Foundation
class HomebrewDiagnostics {
/**
Determines the Homebrew taps the user has installed.
*/
public static var installedTaps: [String] = {
return Shell
.pipe("\(Paths.brew) tap")
.split(separator: "\n")
.map { string in
return String(string)
}
}()
/**
Determines whether the PHP Monitor Cask is installed.
*/
public static var customCaskInstalled: Bool = {
return installedTaps.contains("nicoverbruggen/cask")
}()
/**
It is possible to have the `shivammathur/php` tap installed, and for the core homebrew information to be outdated.
This will then result in two different aliases claiming to point to the same formula (`php`).
@ -55,6 +74,10 @@ class HomebrewDiagnostics {
}
}
/**
This method, unsurprisingly, is supposed to show a specific alert about this alias conflict
with the `shivammathur/php` tap.
*/
public static func presentAlertAboutConflict() {
DispatchQueue.main.async {
BetterAlert()

View File

@ -29,6 +29,12 @@ extension MainMenu {
When the environment is all clear and the app can run, let's go.
*/
private func onEnvironmentPass() {
// Determine install method
Log.info(HomebrewDiagnostics.customCaskInstalled
? "The app has probably been installed via Homebrew Cask."
: "The app has probably been installed directly."
)
// Attempt to find out more info about Valet
if Valet.shared.version != nil {
Log.info("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version!)")
@ -86,8 +92,6 @@ extension MainMenu {
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
Log.info("PHP Monitor is ready to serve!")
// Schedule a request to fetch the PHP version every 60 seconds
DispatchQueue.main.async { [self] in
App.shared.timer = Timer.scheduledTimer(
@ -100,7 +104,12 @@ extension MainMenu {
}
Stats.incrementSuccessfulLaunchCount()
Stats.evaluateSponsorMessageShouldBeDisplayed()
Updater.checkForUpdates()
Log.info("PHP Monitor is ready to serve!")
}
/**

View File

@ -339,32 +339,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
@objc func checkForUpdates() {
DispatchQueue.global(qos: .userInitiated).async {
let caskFile = App.version.contains("-dev")
? Constants.Urls.DevBuildCaskFile.absoluteString
: Constants.Urls.StableBuildCaskFile.absoluteString
let versionString = Shell.pipe(
"curl -s '\(caskFile)' | grep version"
)
guard let onlineVersion = VersionExtractor.from(versionString) else {
Log.err("We couldn't check for updates!")
return
}
guard let current = VersionExtractor.from(App.shortVersion) else {
Log.err("We couldn't parse the current version number!")
return
}
switch onlineVersion.versionCompare(current) {
case .orderedAscending:
Log.info("You are running a newer version of PHP Monitor.")
case .orderedDescending:
Log.info("There is a newer version (\(onlineVersion)) available!")
case .orderedSame:
Log.info("The installed version matches the latest release (\(current)).")
}
Updater.checkForUpdates(background: false)
}
}

View File

@ -20,6 +20,7 @@ enum PreferenceName: String {
case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch"
case allowProtocolForIntegrations = "allow_protocol_for_integrations"
case globalHotkey = "global_hotkey"
case automaticBackgroundUpdateCheck = "backgroundUpdateCheck"
}
/**
@ -76,6 +77,7 @@ class Preferences {
PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true,
PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false,
PreferenceName.allowProtocolForIntegrations.rawValue: true,
PreferenceName.automaticBackgroundUpdateCheck.rawValue: true,
/// Stats
InternalStats.switchCount.rawValue: 0,
InternalStats.launchCount.rawValue: 0,
@ -145,6 +147,8 @@ class Preferences {
forKey: PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue) as Any,
.allowProtocolForIntegrations: UserDefaults.standard.bool(
forKey: PreferenceName.allowProtocolForIntegrations.rawValue) as Any,
.automaticBackgroundUpdateCheck: UserDefaults.standard.bool(
forKey: PreferenceName.automaticBackgroundUpdateCheck.rawValue) as Any,
// Part 2: Always Strings
.globalHotkey: UserDefaults.standard.string(

View File

@ -53,7 +53,8 @@ class PrefsVC: NSViewController {
getAutoRestartPreferenceView(),
getAutomaticComposerUpdatePreferenceView(),
getShortcutPreferenceView(),
getIntegrationsPreferenceView()
getIntegrationsPreferenceView(),
getAutomaticUpdateCheckPreferenceView()
].forEach({ self.stackView.addArrangedSubview($0) })
}
@ -133,6 +134,16 @@ class PrefsVC: NSViewController {
)
}
private func getAutomaticUpdateCheckPreferenceView() -> NSView {
return CheckboxPreferenceView.make(
sectionText: "prefs.updates".localized,
descriptionText: "prefs.automatic_update_check_desc".localized,
checkboxText: "prefs.automatic_update_check_title".localized,
preference: .automaticBackgroundUpdateCheck,
action: {}
)
}
// MARK: - Listening for hotkey delegate
var listeningForHotkeyView: HotkeyPreferenceView?

View File

@ -184,6 +184,7 @@
"prefs.services" = "Services:";
"prefs.switcher" = "Switcher:";
"prefs.integrations" = "Integrations:";
"prefs.updates" = "Updates:";
"prefs.icon_options.php" = "Display PHP Icon";
"prefs.icon_options.elephant" = "Display Elephant Icon";
@ -205,6 +206,9 @@
"prefs.open_protocol_title" = "Allow third-party integrations";
"prefs.open_protocol_desc" = "When checked, this will allow the interaction with third party utilities to work (e.g. Alfred, Raycast). If you disable this, PHP Monitor will still receive the commands, but will not act upon them.";
"prefs.automatic_update_check_title" = "Automatically check for updates";
"prefs.automatic_update_check_desc" = "When checked, PHP Monitor will automatically check if there is a newer version available, and notify you if that is the case.";
"prefs.shortcut_set" = "Set global shortcut";
"prefs.shortcut_listening" = "<listening for keypress>";
@ -373,6 +377,20 @@ You can do this by running `composer global update` in your terminal. After that
"alert.errors.homebrew_permissions.applescript_returned_nil.title" = "Restore Homebrew Permissions has been cancelled.";
"alert.errors.homebrew_permissions.applescript_returned_nil.description" = "The outcome of the script that is executed to adjust the permissions returned nil, which usually means that you did not grant administrative permissions to PHP Monitor.\n\nIf you clicked on Cancel during the authentication prompt, this is normal. If you did actually authenticate and you are still seeing this message, something probably went wrong.";
// CHECK FOR UPDATES
"updater.alerts.newer_version_available.title" = "A newer version of 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. Sometimes, there's even cool new features!";
"updater.alerts.newer_version_available.description" = "PHP Monitor is supposed to be updated via Homebrew, so there is no built-in updater. This check is only meant to inform you of the existence of a new version, you do not need to upgrade.";
"updater.installation_source.brew" = "You appear to have installed PHP Monitor via Homebrew (or have at least tapped the required Caskfile) so it is recommended that you upgrade via the terminal by running `brew upgrade phpmon`.";
"updater.installation_source.direct" = "You do not appear to have installed PHP Monitor via Homebrew, so you will need to visit GitHub to download the latest update.";
"updater.alerts.buttons.release_notes" = "View Release Notes";
"updater.alerts.cannot_check_for_update.title" = "PHP Monitor could not determine if a newer version is available.";
"updater.alerts.cannot_check_for_update.subtitle" = "You might not be connected to the internet, are blocking traffic or GitHub is down and won't allow you to check for updates. If you keep seeing this message, you may want to manually check the releases page.";
"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";
// WARNINGS ABOUT NON-DEFAULT TLD
"alert.warnings.tld_issue.title" = "You are not using `.test` as the TLD for Valet.";