diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index da15604..762151a 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -1741,7 +1741,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 965; + CURRENT_PROJECT_VERSION = 973; DEAD_CODE_STRIPPING = YES; DEBUG = YES; DEVELOPMENT_TEAM = 8M54J5J787; @@ -1752,7 +1752,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 5.6.0; + MARKETING_VERSION = 5.6.1; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1769,7 +1769,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 965; + CURRENT_PROJECT_VERSION = 973; DEAD_CODE_STRIPPING = YES; DEBUG = NO; DEVELOPMENT_TEAM = 8M54J5J787; @@ -1780,7 +1780,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 5.6.0; + MARKETING_VERSION = 5.6.1; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/phpmon-tests/Versions/AppUpdaterCheckTest.swift b/phpmon-tests/Versions/AppUpdaterCheckTest.swift index 64a2854..a108ac5 100644 --- a/phpmon-tests/Versions/AppUpdaterCheckTest.swift +++ b/phpmon-tests/Versions/AppUpdaterCheckTest.swift @@ -18,4 +18,30 @@ class AppUpdaterCheckTest: XCTestCase { XCTAssertNotNil(version) } + func testTaggedReleaseOmitsZeroPatch() { + let version = AppVersion.from("3.5.0_333")! + + XCTAssertEqual(version.tagged, "3.5") + XCTAssertEqual(version.version, "3.5.0") + } + + func testTaggedReleaseDoesntOmitNonZeroPatch() { + let version = AppVersion.from("3.5.1_333")! + + XCTAssertEqual(version.tagged, "3.5.1") + XCTAssertEqual(version.version, "3.5.1") + } + + func testTagTruncationDoesntAffectMajorVersions() { + var version = AppVersion.from("5.0_333")! + + XCTAssertEqual(version.tagged, "5.0") + XCTAssertEqual(version.version, "5.0") + + version = AppVersion.from("5.0.0_333")! + + XCTAssertEqual(version.tagged, "5.0") + XCTAssertEqual(version.version, "5.0.0") + } + } diff --git a/phpmon-tests/Versions/PhpVersionNumberTest.swift b/phpmon-tests/Versions/PhpVersionNumberTest.swift index 1f4b8c4..f8b904a 100644 --- a/phpmon-tests/Versions/PhpVersionNumberTest.swift +++ b/phpmon-tests/Versions/PhpVersionNumberTest.swift @@ -8,6 +8,7 @@ import XCTest +// swiftlint:disable type_body_length class PhpVersionNumberTest: XCTestCase { func testCanDeconstructPhpVersion() throws { @@ -287,4 +288,76 @@ class PhpVersionNumberTest: XCTestCase { .make(from: ["7.3.1", "7.2.9"]).all ) } + + func testCanCheckLessThanOrEqualConstraints() throws { + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "<=7.2", strict: true), + PhpVersionNumberCollection + .make(from: ["7.2", "7.1", "7.0"]).all + ) + + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "<=7.2.0", strict: true), + PhpVersionNumberCollection + .make(from: ["7.2", "7.1", "7.0"]).all + ) + + // Strict check (>7.2.5 is too new for 7.2 which resolves to 7.2.0) + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "<=7.2.5", strict: true), + PhpVersionNumberCollection + .make(from: ["7.2", "7.1", "7.0"]).all + ) + + // Non-strict check (ignoring patch has no effect) + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "<=7.2.5", strict: false), + PhpVersionNumberCollection + .make(from: ["7.2", "7.1", "7.0"]).all + ) + } + + func testCanCheckLessThanConstraints() throws { + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "<7.2", strict: true), + PhpVersionNumberCollection + .make(from: ["7.1", "7.0"]).all + ) + + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "<7.2.0", strict: true), + PhpVersionNumberCollection + .make(from: ["7.1", "7.0"]).all + ) + + // Strict check (>7.2.5 is too new for 7.2 which resolves to 7.2.0) + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "<7.2.5", strict: true), + PhpVersionNumberCollection + .make(from: ["7.2", "7.1", "7.0"]).all + ) + + // Non-strict check (patch resolves to 7.2.999, which is bigger than 7.2.5) + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "<7.2.5", strict: false), + PhpVersionNumberCollection + .make(from: ["7.1", "7.0"]).all + ) + } } diff --git a/phpmon/Common/Core/Shell+PATH.swift b/phpmon/Common/Core/Shell+PATH.swift index cd29071..7f78c98 100644 --- a/phpmon/Common/Core/Shell+PATH.swift +++ b/phpmon/Common/Core/Shell+PATH.swift @@ -14,12 +14,18 @@ extension Shell { let task = Process() task.launchPath = "/bin/zsh" - // We need an interactive shell so the user's PATH is loaded in correctly - task.arguments = ["--login", "-ilc", "echo $PATH"] + let command = Filesystem.fileExists("~/.zshrc") + // source the user's .zshrc file if it exists to complete $PATH + ? ". ~/.zshrc && echo $PATH" + // otherwise, non-interactive mode is sufficient + : "echo $PATH" + + task.arguments = ["--login", "-lc", command] let pipe = Pipe() task.standardOutput = pipe task.launch() + let data = pipe.fileHandleForReading.readDataToEndOfFile() return String(data: data, encoding: String.Encoding.utf8) ?? "" diff --git a/phpmon/Common/PHP/PHP Version/PhpVersionNumber.swift b/phpmon/Common/PHP/PHP Version/PhpVersionNumber.swift index 4241648..c685aaf 100644 --- a/phpmon/Common/PHP/PHP Version/PhpVersionNumber.swift +++ b/phpmon/Common/PHP/PHP Version/PhpVersionNumber.swift @@ -87,6 +87,14 @@ public struct PhpVersionNumberCollection: Equatable { return self.versions.filter { $0.isNewerThan(version, strict) } } + if let version = PhpVersionNumber.make(from: constraint, type: .smallerThanOrEqual) { + return self.versions.filter { $0.isSameAs(version, strict) || $0.isOlderThan(version, strict)} + } + + if let version = PhpVersionNumber.make(from: constraint, type: .smallerThan) { + return self.versions.filter { $0.isOlderThan(version, strict)} + } + return [] } } @@ -116,12 +124,8 @@ public struct PhpVersionNumber: Equatable, Hashable { case tildeVersionRange = #"^~(?\d+).(?\d+).?(?\d+)?\z"# case greaterThanOrEqual = #"^>=(?\d+).(?\d+).?(?\d+)?\z"# case greaterThan = #"^>(?\d+).(?\d+).?(?\d+)?\z"# - - // TODO: (6.0) Handle these cases (even though I suspect these are uncommon) - /* case smallerThanOrEqual = #"^<=(?\d+).(?\d+).?(?\d+)?\z"# case smallerThan = #"^<(?\d+).(?\d+).?(?\d+)?\z"# - */ } public static func parse(_ text: String) throws -> Self { @@ -175,6 +179,15 @@ public struct PhpVersionNumber: Equatable, Hashable { ) } + internal func isOlderThan(_ version: PhpVersionNumber, _ strict: Bool) -> Bool { + return ( + self.major < version.major || + self.major == version.major && self.minor < version.minor || + self.major == version.major && self.minor == version.minor + && self.patch(strict) < version.patch(strict) + ) + } + internal func hasNewerMinorVersionOrPatch(_ version: PhpVersionNumber, _ strict: Bool) -> Bool { return self.major == version.major && ( diff --git a/phpmon/Domain/App/AppUpdateChecker.swift b/phpmon/Domain/App/AppUpdateChecker.swift index 7ac48da..9914e15 100644 --- a/phpmon/Domain/App/AppUpdateChecker.swift +++ b/phpmon/Domain/App/AppUpdateChecker.swift @@ -146,8 +146,9 @@ class AppUpdateChecker { text: "updater.alerts.buttons.release_notes".localized, action: { vc in vc.close(with: .OK) + NSWorkspace.shared.open( - Constants.Urls.GitHubReleases.appendingPathComponent("/tag/v\(version.version)\(devSuffix)") + Constants.Urls.GitHubReleases.appendingPathComponent("/tag/v\(version.tagged)\(devSuffix)") ) } ) diff --git a/phpmon/Domain/App/AppVersion.swift b/phpmon/Domain/App/AppVersion.swift index c554b2c..928858a 100644 --- a/phpmon/Domain/App/AppVersion.swift +++ b/phpmon/Domain/App/AppVersion.swift @@ -66,6 +66,14 @@ class AppVersion { return AppVersion.from("\(App.shortVersion)_\(App.bundleVersion)")! } + var tagged: String { + if version.suffix(2) == ".0" && version.count > 3 { + return String(version.dropLast(2)) + } + + return version + } + var computerReadable: String { return "\(version)_\(build ?? "0")" } diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 6cc8e4b..08072a8 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -155,11 +155,11 @@ extension MainMenu { App.shared.detectedApplications = Application.detectPresetApplications() - let customApps = Preferences.custom.scanApps.map { appName in + let customApps = Preferences.custom.scanApps?.map { appName in return Application(appName, .user_supplied) }.filter { app in return app.isInstalled() - } + } ?? [] App.shared.detectedApplications.append(contentsOf: customApps) diff --git a/phpmon/Domain/Menu/StatusMenu+Items.swift b/phpmon/Domain/Menu/StatusMenu+Items.swift index 07a28d7..1bc2971 100644 --- a/phpmon/Domain/Menu/StatusMenu+Items.swift +++ b/phpmon/Domain/Menu/StatusMenu+Items.swift @@ -216,14 +216,14 @@ extension StatusMenu { } addItems([ - NSMenuItem.separator(), NSMenuItem(title: "mi_xdebug_mode".localized, submenu: [ HeaderView.asMenuItem(text: "mi_xdebug_available_modes".localized) ] + Xdebug.asMenuItems() + [ HeaderView.asMenuItem(text: "mi_xdebug_actions".localized), NSMenuItem(title: "mi_xdebug_disable_all".localized, action: #selector(MainMenu.disableAllXdebugModes)) - ], target: MainMenu.shared) + ], target: MainMenu.shared), + NSMenuItem.separator() ], target: MainMenu.shared) } diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 70d5d18..2dd9da9 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -48,8 +48,9 @@ class StatusMenu: NSMenu { if Preferences.isEnabled(.displayExtensions) { addExtensionsMenuItems() - addXdebugMenuItem() NSMenuItem.separator() + + addXdebugMenuItem() } addPhpDoctorMenuItem() diff --git a/phpmon/Domain/Preferences/CustomPrefs.swift b/phpmon/Domain/Preferences/CustomPrefs.swift index 22d2c24..f56dcd4 100644 --- a/phpmon/Domain/Preferences/CustomPrefs.swift +++ b/phpmon/Domain/Preferences/CustomPrefs.swift @@ -9,7 +9,7 @@ import Foundation struct CustomPrefs: Decodable { - let scanApps: [String] + let scanApps: [String]? let presets: [Preset]? let services: [String]? let environmentVariables: [String: String]? diff --git a/phpmon/Domain/SwiftUI/Common/SwiftUIHelper.swift b/phpmon/Domain/SwiftUI/Common/SwiftUIHelper.swift index 332cc83..3598836 100644 --- a/phpmon/Domain/SwiftUI/Common/SwiftUIHelper.swift +++ b/phpmon/Domain/SwiftUI/Common/SwiftUIHelper.swift @@ -10,8 +10,13 @@ import Foundation import SwiftUI var isRunningSwiftUIPreview: Bool { - return ProcessInfo.processInfo - .environment["XCODE_RUNNING_FOR_PREVIEWS"] != nil + #if DEBUG + // If running SwiftUI *and* when debugging + return ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != nil + #else + // Release builds should always return false here + return false + #endif } extension Color {