From 4ba2b25f18809e7edfc44990069d7486b3a0558e Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 10 May 2022 18:09:22 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=8C=20App=20version=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 10 +++ phpmon-tests/Versions/AppVersionTest.swift | 53 +++++++++++++++ phpmon/Domain/App/App.swift | 10 ++- phpmon/Domain/App/AppUpdateChecker.swift | 22 ++++--- phpmon/Domain/App/AppVersion.swift | 76 ++++++++++++++++++++++ 5 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 phpmon-tests/Versions/AppVersionTest.swift create mode 100644 phpmon/Domain/App/AppVersion.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 8fb9e2a..29e80d0 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -52,6 +52,9 @@ C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; }; C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; }; C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; }; + C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE736282ABA4F00A302C2 /* AppVersion.swift */; }; + C40FE738282ABA4F00A302C2 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE736282ABA4F00A302C2 /* AppVersion.swift */; }; + C40FE73B282ABB2E00A302C2 /* AppVersionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE739282ABB2E00A302C2 /* AppVersionTest.swift */; }; C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; }; C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */; }; C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */; }; @@ -296,6 +299,8 @@ C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = ""; }; C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = ""; }; C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; + C40FE736282ABA4F00A302C2 /* AppVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersion.swift; sourceTree = ""; }; + C40FE739282ABB2E00A302C2 /* AppVersionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionTest.swift; sourceTree = ""; }; C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = ""; }; C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpFrameworks.swift; sourceTree = ""; }; C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = ""; }; @@ -795,6 +800,7 @@ C4EED88827A48778006D7272 /* InterAppHandler.swift */, C4D8016522B1584700C6DA1B /* Startup.swift */, C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */, + C40FE736282ABA4F00A302C2 /* AppVersion.swift */, ); path = App; sourceTree = ""; @@ -876,6 +882,7 @@ C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */, C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */, C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */, + C40FE739282ABB2E00A302C2 /* AppVersionTest.swift */, C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */, ); path = Versions; @@ -1160,6 +1167,7 @@ C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */, C4C0E8DF27F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */, C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */, + C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */, C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, C4B585442770FE3900DA4FBE /* Command.swift in Sources */, C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */, @@ -1301,6 +1309,7 @@ C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */, C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */, C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */, + C40FE738282ABA4F00A302C2 /* AppVersion.swift in Sources */, C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */, C4AF9F78275447F100D44ED0 /* ValetConfigurationTest.swift in Sources */, @@ -1357,6 +1366,7 @@ C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */, C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */, C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */, + C40FE73B282ABB2E00A302C2 /* AppVersionTest.swift in Sources */, C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */, C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */, C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */, diff --git a/phpmon-tests/Versions/AppVersionTest.swift b/phpmon-tests/Versions/AppVersionTest.swift new file mode 100644 index 0000000..ad1a880 --- /dev/null +++ b/phpmon-tests/Versions/AppVersionTest.swift @@ -0,0 +1,53 @@ +// +// AppVersionTest.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 10/05/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import XCTest + +class AppVersionTest: XCTestCase { + + func testCanRetrieveInternalAppVersion() { + XCTAssertNotNil(AppVersion.fromCurrentVersion()) + } + + func testCanParseNormalVersionString() { + let version = AppVersion.from("1.0.0") + + XCTAssertNotNil(version) + XCTAssertEqual("1.0.0", version?.version) + XCTAssertEqual(nil, version?.build) + XCTAssertEqual(nil, version?.suffix) + } + + func testCanParseDevVersionStringWithoutBuildNumber() { + let version = AppVersion.from("1.0.0-dev") + + XCTAssertNotNil(version) + XCTAssertEqual("1.0.0", version?.version) + XCTAssertEqual(nil, version?.build) + XCTAssertEqual("dev", version?.suffix) + } + + func testCanParseDevVersionStringWithBuildNumber() { + let version = AppVersion.from("1.0.0-dev,870") + + XCTAssertNotNil(version) + XCTAssertEqual("1.0.0", version?.version) + XCTAssertEqual("870", version?.build) + XCTAssertEqual("dev", version?.suffix) + } + + func testCanParseUnderscoresAsBuildSeparatorToo() { + let version = AppVersion.from("1.0.0-dev_870") + + XCTAssertNotNil(version) + XCTAssertEqual("1.0.0", version?.version) + XCTAssertEqual("870", version?.build) + XCTAssertEqual("dev", version?.suffix) + } + +} diff --git a/phpmon/Domain/App/App.swift b/phpmon/Domain/App/App.swift index 6e16018..51bfc89 100644 --- a/phpmon/Domain/App/App.swift +++ b/phpmon/Domain/App/App.swift @@ -21,10 +21,14 @@ class App { return "\(version) (\(build))" } - /** Retrieve the version number from the main info dictionary, Info.plist, but without the build number. */ + /** Just the bundle version (build). */ + static var bundleVersion: String { + return Bundle.main.infoDictionary?["CFBundleVersion"] as! String + } + + /** Just the version number. */ static var shortVersion: String { - let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String - return "\(version)" + return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String } static var architecture: String { diff --git a/phpmon/Domain/App/AppUpdateChecker.swift b/phpmon/Domain/App/AppUpdateChecker.swift index 9819ee8..ad0617e 100644 --- a/phpmon/Domain/App/AppUpdateChecker.swift +++ b/phpmon/Domain/App/AppUpdateChecker.swift @@ -19,7 +19,9 @@ class AppUpdateChecker { return App.version.contains("-dev") }() - public static func retrieveVersionFromCask(_ initiatedFromBackground: Bool = true) -> String { + public static func retrieveVersionFromCask( + _ initiatedFromBackground: Bool = true + ) -> String { let caskFile = App.version.contains("-dev") ? Constants.Urls.DevBuildCaskFile.absoluteString : Constants.Urls.StableBuildCaskFile.absoluteString @@ -35,7 +37,9 @@ class AppUpdateChecker { ) } - public static func checkIfNewerVersionIsAvailable(initiatedFromBackground: Bool = true) { + public static func checkIfNewerVersionIsAvailable( + initiatedFromBackground: Bool = true + ) { if initiatedFromBackground { if !Preferences.isEnabled(.automaticBackgroundUpdateCheck) { Log.info("Automatic updates are disabled. No check will be performed.") @@ -63,7 +67,11 @@ class AppUpdateChecker { return } - handleVersionComparison(currentVersion, onlineVersion, initiatedFromBackground) + handleVersionComparison( + currentVersion, + onlineVersion, + initiatedFromBackground + ) } private static func handleVersionComparison( @@ -74,17 +82,13 @@ class AppUpdateChecker { switch onlineVersion.versionCompare(currentVersion) { case .orderedAscending: Log.info("You are running a newer version of PHP Monitor.") - if !background { - notifyVersionDoesNotNeedUpgrade() - } + if !background { notifyVersionDoesNotNeedUpgrade() } case .orderedDescending: Log.info("There is a newer version (\(onlineVersion)) available!") notifyAboutNewerVersion(version: onlineVersion) case .orderedSame: Log.info("The installed version (\(currentVersion)) matches the latest release (\(onlineVersion)).") - if !background { - notifyVersionDoesNotNeedUpgrade() - } + if !background { notifyVersionDoesNotNeedUpgrade() } } } diff --git a/phpmon/Domain/App/AppVersion.swift b/phpmon/Domain/App/AppVersion.swift new file mode 100644 index 0000000..6e04fbe --- /dev/null +++ b/phpmon/Domain/App/AppVersion.swift @@ -0,0 +1,76 @@ +// +// AppVersion.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 10/05/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class AppVersion { + var version: String + var build: String? + var suffix: String? + + init(version: String, build: String?, suffix: String? = nil) { + self.version = version + self.build = build + self.suffix = suffix + } + + public static func from(_ string: String) -> AppVersion? { + do { + let regex = try NSRegularExpression( + pattern: #"(?(\d+)[.](\d+)([.](\d+))?)(-(?[a-z]+)){0,1}((,|_)(?\d+)){0,1}"#, + options: [] + ) + + let match = regex.matches( + in: string, + options: [], + range: NSRange(location: 0, length: string.count) + ).first + + guard let match = match else { + return nil + } + + var version: String = "" + var build: String? + var suffix: String? + + if let versionRange = Range(match.range(withName: "version"), in: string) { + version = String(string[versionRange]) + } + + if let buildRange = Range(match.range(withName: "build"), in: string) { + build = String(string[buildRange]) + } + + if let suffixRange = Range(match.range(withName: "suffix"), in: string) { + suffix = String(string[suffixRange]) + } + + return AppVersion( + version: version, + build: build, + suffix: suffix + ) + } catch { + return nil + } + } + + public static func fromCurrentVersion() -> AppVersion { + return AppVersion( + version: App.shortVersion, + build: App.bundleVersion + ) + } + + var comparable: String { + return "\(version).\(build)" + } + +}