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

Compare commits

...

34 Commits
v5.6 ... v5.6.5

Author SHA1 Message Date
e29ca9e1ad 🚀 Version 5.6.5 2022-11-27 14:37:15 +01:00
40e05d9445 🔧 Bump build 2022-11-27 14:04:15 +01:00
3ebf51b319 👌 Fix threading issue with Composer update (#212) 2022-11-27 14:03:46 +01:00
4b8ad911f1 Add support for nginx-full (#211) 2022-11-21 18:10:33 +01:00
efd902b4f3 🚀 Version 5.6.4 2022-11-18 18:39:00 +01:00
918e272da7 🔧 Bump build 2022-11-18 18:37:31 +01:00
272a9182d3 🔥 Remove reference to LatestStablePhpVersion 2022-11-18 18:37:20 +01:00
63f85aff91 🐛 Fix issue when using services quick toggle 2022-11-18 18:32:38 +01:00
581c2d1974 📝 Update supported PHP versions in README 2022-11-07 20:45:58 +01:00
4deef64537 📝 Update SECURITY.md 2022-11-02 21:08:57 +01:00
bedabaa3bb 👌 PHP 8.2 release, PHP 8.3-dev support 2022-11-02 21:06:36 +01:00
9da3772212 🚀 Version 5.6.3 2022-10-22 17:34:53 +02:00
e62b03d070 👌 Style fix 2022-10-22 16:43:05 +02:00
9a11d2efed 🔧 Bump build 2022-10-22 16:42:26 +02:00
b134e62328 🐛 Handle empty output for brew info 2022-10-22 16:41:54 +02:00
5c69133c42 👌 Add brew tap homebrew/services instruction
This now recommends the appropriate solution for #208.
2022-10-20 20:44:44 +02:00
f4448e0640 🔧 Bump version number 2022-10-10 21:49:51 +02:00
7fd30d7c54 Add preference to disable TLD alert (#206) 2022-10-10 21:49:43 +02:00
2c57dea97f 🔀 Merge branch 'main' into dev/5.6 2022-10-09 22:00:36 +02:00
a77fa5557a 📝 Update README for Login Items on Ventura 2022-10-09 21:56:13 +02:00
45704fc736 🚀 Version 5.6.2 2022-10-02 13:28:58 +02:00
f28354e634 🐛 Use valet secure sitename (#197) 2022-10-02 13:28:01 +02:00
8055a32bde 🐛 Fix ComposerWindow deinit not firing 2022-09-29 18:50:40 +02:00
5b3054326e 🔧 Bump version number 2022-09-28 18:24:26 +02:00
e7f3c7e59c 🐛 Fix an issue with missing separator item 2022-09-28 18:24:01 +02:00
a407515534 🚀 Version 5.6.1 2022-09-24 12:56:14 +02:00
b827ffb869 🔧 Bump version number 2022-09-23 16:46:23 +02:00
bdb718598e 🐛 Various bugfixes
- Fixes issue with `scanApps` being non-optional in custom configuration
- Fixes issue with position of separator if Xdebug is not detected
- Ensure that `isRunningSwiftUIPreview` modifier always return false for debug builds
2022-09-23 16:46:13 +02:00
ddfc73e033 🐛 Resolve issue with determining PATH (#194)
In previous builds, PHP Monitor would use interactive mode when opening
a /bin/zsh shell, in order to be able to load the full PATH.

This is a problem because when launching PHP Monitor via the command
line or when you have some interactivity in an actual interactive
shell, it is possible to get stuck waiting for a particular input
which will effectively 'freeze' PHP Monitor's shell.

There are two options to fix this:
1) work with a timeout, which may or may not return a PATH
2) use a non-interactive shell and source .zshrc

I chose option 2, which is the more robust choice. If no .zshrc file
exists, it is also not sourced to avoid warnings or errors from ending
up in the PATH.
2022-09-23 16:44:06 +02:00
cfae520984 👌 Correctly resolve tagged version (#195) 2022-09-22 18:43:09 +02:00
0c176493e5 🔀 Merge branch 'main' into dev/5.6 2022-09-18 14:08:48 +02:00
71da62f954 📝 Update contribution guidelines 2022-09-18 14:08:10 +02:00
d6781568a3 📝 Update README 2022-09-18 13:39:40 +02:00
c9c7e14416 👌 Add support for <= and < version constraints
I am entirely unsure why one would need these, but I figured I'd get
these in the app before I start the work on PHP Monitor 6.0.

This ensures all common version constraints can now be parsed correctly.
2022-09-18 00:05:37 +02:00
38 changed files with 314 additions and 84 deletions

23
.github/contributing.md vendored Normal file
View File

@ -0,0 +1,23 @@
# Contribution Guidelines
Thank you for your interest in contributing to PHP Monitor.
I consider this project a bit of a nice side-project to my daily gig, so it is very much a personal affair where I love to tinker around.
**While the code of the latest PHP Monitor release is public, many things are constantly in flux that may not be pushed to this repository yet.**
I don't mean to be rude, but I don't want other people involved with the project beyond simply contributing a few small things here and there, as has been the case in the past.
The extra mental overhead of having additional contributors to report to, whose code will need to be reviewed... it's a lot and it makes working on PHP Monitor less enjoyable for me.
Plus, at this point, the majority of PHP Monitor's main functionality is also done.
As a result, I may refer you to this file at some point. Again, I don't wish to be rude, but this general rule stands:
**Making any changes in a fork and opening a pull request without opening an issue first will most likely result in your PR being closed without mercy.**
To repeat, I am **not opposed** to small contributions and fixes, if they are **meaningful or insightful**.
To learn more, please check out the [pull request template](/.github/pull_request_template.md) which contains more information about my contribution requirements. (This will also show up when you open a new PR.)
Thank you for respecting this!

View File

@ -16,7 +16,7 @@ In short: It is usually best to *get in touch first* if you are making substanti
## About destination branches
Please keep in mind that `main` is reserved for the current code state of the latest release and should *never* be the destination branch unless a new release is happening. **Merge requests that target `main` will be closed without mercy.**
Please keep in mind that `main` is reserved for the current code state of the latest release and should *never* be the destination branch unless a new release is happening. **Pull requests that target `main` will be closed without mercy.**
Usually, the best target is the stable `dev/x.x` branch that corresponds with the latest major version that is released.

View File

@ -244,6 +244,8 @@
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
C4D27F24292BDF630081B38F /* Homebrew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D27F23292BDF630081B38F /* Homebrew.swift */; };
C4D27F25292BDF630081B38F /* Homebrew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D27F23292BDF630081B38F /* Homebrew.swift */; };
C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */; };
C4D5CFCB27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */; };
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
@ -462,6 +464,7 @@
C4CDA892288F1A71007CE25F /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = "<group>"; };
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = "<group>"; };
C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerWindow.swift; sourceTree = "<group>"; };
C4D27F23292BDF630081B38F /* Homebrew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Homebrew.swift; sourceTree = "<group>"; };
C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationFile.swift; sourceTree = "<group>"; };
C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; };
C4D89BC52783C99400A02B68 /* ComposerJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerJson.swift; sourceTree = "<group>"; };
@ -644,6 +647,7 @@
C4C1019A27C65C6F001FACC2 /* Process.swift */,
C40C7F2F27722E8D00DDDCDC /* Logger.swift */,
C417DC73277614690015E6EE /* Helpers.swift */,
C4D27F23292BDF630081B38F /* Homebrew.swift */,
);
path = Core;
sourceTree = "<group>";
@ -1413,6 +1417,7 @@
C476FF9822B0DD830098105B /* Alert.swift in Sources */,
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */,
C4D27F24292BDF630081B38F /* Homebrew.swift in Sources */,
C485707028BF452300539B36 /* WarningsWindowController.swift in Sources */,
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
@ -1555,6 +1560,7 @@
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */,
C485707C28BF459500539B36 /* NoWarningsView.swift in Sources */,
C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */,
C4D27F25292BDF630081B38F /* Homebrew.swift in Sources */,
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
C4C0E8E027F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */,
@ -1741,7 +1747,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 965;
CURRENT_PROJECT_VERSION = 991;
DEAD_CODE_STRIPPING = YES;
DEBUG = YES;
DEVELOPMENT_TEAM = 8M54J5J787;
@ -1752,7 +1758,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 5.6.0;
MARKETING_VERSION = 5.6.5;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1769,7 +1775,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 965;
CURRENT_PROJECT_VERSION = 991;
DEAD_CODE_STRIPPING = YES;
DEBUG = NO;
DEVELOPMENT_TEAM = 8M54J5J787;
@ -1780,7 +1786,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 5.6.0;
MARKETING_VERSION = 5.6.5;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

@ -1,5 +1,5 @@
> **Note**
> If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider leaving [a one-time donation](https://nicoverbruggen.be/sponsor) to support the project, as this is something I make in my free time. **Thank you!** ⭐️
> If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider [sponsoring](https://nicoverbruggen.be/sponsor) to support the project, as this is something I make in my free time. **Thank you!** ⭐️
<p align="center"><img src="./docs/logo.png" alt="PHP Monitor Logo" width="500px" /></p>
@ -88,7 +88,8 @@ If you're still having issues, here's a few common questions & answers, as well
<li>PHP 7.4</li>
<li>PHP 8.0</li>
<li>PHP 8.1</li>
<li>PHP 8.2 (experimental)</li>
<li>PHP 8.2</li>
<li>PHP 8.3 (experimental)</li>
</ul>
For more details, consult the [constants file](https://github.com/nicoverbruggen/phpmon/blob/main/phpmon/Common/Core/Constants.swift#L16) file to see which versions are supported.
@ -98,7 +99,9 @@ For more details, consult the [constants file](https://github.com/nicoverbruggen
<details>
<summary><strong>I want PHP Monitor to start up when I boot my Mac!</strong></summary>
You can do this by dragging *PHP Monitor.app* into the **Login Items** section in **System Preferences > Users & Groups** for your account.
On macOS Ventura, you can accomplish this by going to **System Settings > General > Login Items** and adding PHP Monitor.app to the list **Open at Login**. You can do this with any application, by the way.
On older versions of macOS, you can do this by dragging *PHP Monitor.app* into the **Login Items** section in **System Preferences > Users & Groups** for your account.
Super convenient!
</details>

View File

@ -6,9 +6,7 @@ Generally speaking, only the latest version of **PHP Monitor** is supported, exc
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Recommended Valet Version |
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
| 5.x | ✅ Universal binary | ✅ Yes | Big Sur (11.0)<br/>Monterey (12.0)<br/>Ventura (13.0)* | macOS 11+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x) | 3.0 recommended<br/> 2.16.2 minimum |
_(*) macOS Ventura (13.0) is not officially supported until it officially releases._
| 5.x | ✅ Universal binary | ✅ Yes | Big Sur (11.0)<br/>Monterey (12.0)<br/>Ventura (13.0) | macOS 11+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x) | 3.0 recommended<br/> 2.16.2 minimum |
## Legacy versions

View File

@ -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")
}
}

View File

@ -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
)
}
}

View File

@ -13,37 +13,35 @@ class Actions {
// MARK: - Services
public static func restartPhpFpm() {
brew("services restart \(PhpEnv.phpInstall.formula)", sudo: true)
brew("services restart \(Homebrew.Formulae.php)", sudo: true)
}
public static func restartNginx() {
brew("services restart nginx", sudo: true)
brew("services restart \(Homebrew.Formulae.nginx)", sudo: true)
}
public static func restartDnsMasq() {
brew("services restart dnsmasq", sudo: true)
brew("services restart \(Homebrew.Formulae.dnsmasq)", sudo: true)
}
public static func stopValetServices() {
brew("services stop \(PhpEnv.phpInstall.formula)", sudo: true)
brew("services stop nginx", sudo: true)
brew("services stop dnsmasq", sudo: true)
brew("services stop \(Homebrew.Formulae.php)", sudo: true)
brew("services stop \(Homebrew.Formulae.nginx)", sudo: true)
brew("services stop \(Homebrew.Formulae.dnsmasq)", sudo: true)
}
public static func fixHomebrewPermissions() throws {
var servicesCommands = [
"\(Paths.brew) services stop nginx",
"\(Paths.brew) services stop dnsmasq"
"\(Paths.brew) services stop \(Homebrew.Formulae.nginx)",
"\(Paths.brew) services stop \(Homebrew.Formulae.dnsmasq)"
]
var cellarCommands = [
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/nginx",
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/dnsmasq"
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(Homebrew.Formulae.nginx)",
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(Homebrew.Formulae.dnsmasq)"
]
PhpEnv.shared.availablePhpVersions.forEach { version in
let formula = version == PhpEnv.brewPhpVersion
? "php"
: "php@\(version)"
let formula = version == PhpEnv.brewPhpVersion ? "php" : "php@\(version)"
servicesCommands.append("\(Paths.brew) services stop \(formula)")
cellarCommands.append("chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(formula)")
}
@ -145,9 +143,9 @@ class Actions {
*/
public static func fixMyValet(completed: @escaping () -> Void) {
InternalSwitcher().performSwitch(to: PhpEnv.brewPhpVersion, completion: {
brew("services restart dnsmasq", sudo: true)
brew("services restart php", sudo: true)
brew("services restart nginx", sudo: true)
brew("services restart \(Homebrew.Formulae.dnsmasq)", sudo: true)
brew("services restart \(Homebrew.Formulae.php)", sudo: true)
brew("services restart \(Homebrew.Formulae.nginx)", sudo: true)
completed()
})
}

View File

@ -9,12 +9,6 @@ import Cocoa
struct Constants {
/**
* The latest PHP version that is considered to be stable at the time of release.
* This version number is currently not used (only as a default fallback).
*/
static let LatestStablePhpVersion = "8.1"
/**
The minimum version of Valet that is recommended.
If the installed version is older, a notification will be shown
@ -42,13 +36,14 @@ struct Constants {
"7.4",
"8.0",
"8.1",
"8.2",
// ====================
// EXPERIMENTAL SUPPORT
// ====================
// Every release that supports the next release will always support the next
// dev release. In this case, that means that the version below is detected.
"8.2"
"8.3"
]
struct Urls {

View File

@ -0,0 +1,25 @@
//
// Homebrew.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 21/11/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
class Homebrew {
struct Formulae {
static var php: String {
return PhpEnv.phpInstall.formula
}
static var nginx: String {
return HomebrewDiagnostics.usesNginxFullFormula ? "nginx-full" : "nginx"
}
static var dnsmasq: String {
return "dnsmasq"
}
}
}

View File

@ -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) ?? ""

View File

@ -30,7 +30,7 @@ class PMWindowController: NSWindowController, NSWindowDelegate {
}
deinit {
Log.perf("Window controller '\(windowName)' was deinitialized")
Log.perf("deinit: \(String(describing: self)).\(#function)")
}
}

View File

@ -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 = #"^~(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
case greaterThanOrEqual = #"^>=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
case greaterThan = #"^>(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
// TODO: (6.0) Handle these cases (even though I suspect these are uncommon)
/*
case smallerThanOrEqual = #"^<=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
case smallerThan = #"^<(?<major>\d+).(?<minor>\d+).?(?<patch>\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 &&
(

View File

@ -46,7 +46,7 @@ class InternalSwitcher: PhpSwitcher {
}
Log.info("Restarting nginx, just to be sure!")
brew("services restart nginx", sudo: true)
brew("services restart \(Homebrew.Formulae.nginx)", sudo: true)
Log.info("The new version(s) have been linked!")
completion()

View File

@ -65,7 +65,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
override init() {
logger.verbosity = .info
#if DEBUG
// logger.verbosity = .performance
logger.verbosity = .performance
#endif
if CommandLine.arguments.contains("--v") {
logger.verbosity = .performance

View File

@ -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)")
)
}
)

View File

@ -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")"
}

View File

@ -18,9 +18,9 @@ class ServicesManager: ObservableObject {
public static func loadHomebrewServices(completed: (() -> Void)? = nil) {
let rootServiceNames = [
PhpEnv.phpInstall.formula,
"nginx",
"dnsmasq"
Homebrew.Formulae.php,
Homebrew.Formulae.nginx,
Homebrew.Formulae.dnsmasq
]
DispatchQueue.global(qos: .background).async {
@ -39,9 +39,7 @@ class ServicesManager: ObservableObject {
}
}
guard let userServiceNames = Preferences.custom.services else {
return
}
let userServiceNames = Preferences.custom.services ?? []
DispatchQueue.global(qos: .background).async {
let data = Shell

View File

@ -160,7 +160,7 @@ class Startup {
// Verify if the Homebrew services are running (as root).
// =================================================================================
EnvironmentCheck(
command: { return HomebrewDiagnostics.cannotLoadService() },
command: { return HomebrewDiagnostics.cannotLoadService("dnsmasq") },
name: "`sudo \(Paths.brew) services info` JSON loaded",
titleText: "startup.errors.services_json_error.title".localized,
subtitleText: "startup.errors.services_json_error.subtitle".localized,

View File

@ -47,7 +47,7 @@ extension DomainListVC {
let originalSecureStatus = selectedSite!.secured
let action = selectedSite!.secured ? "unsecure" : "secure"
let selectedSite = selectedSite!
let command = "cd '\(selectedSite.absolutePath)' && sudo \(Paths.valet) \(action) && exit;"
let command = "sudo \(Paths.valet) \(action) '\(selectedSite.name)' && exit;"
waitAndExecute {
Shell.run(command, requiresPath: true)

View File

@ -292,6 +292,6 @@ class DomainListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource
// MARK: - Deinitialization
deinit {
Log.perf("DomainListVC deallocated")
Log.perf("deinit: \(String(describing: self)).\(#function)")
}
}

View File

@ -52,15 +52,15 @@ class ComposerWindow {
}
task.listen(
didReceiveStandardOutputData: { string in
didReceiveStandardOutputData: { [weak self] string in
DispatchQueue.main.async {
self.window?.addToConsole(string)
self?.window?.addToConsole(string)
}
// Log.perf("\(string.trimmingCharacters(in: .newlines))")
},
didReceiveStandardErrorData: { string in
didReceiveStandardErrorData: { [weak self] string in
DispatchQueue.main.async {
self.window?.addToConsole(string)
self?.window?.addToConsole(string)
}
// Log.perf("\(string.trimmingCharacters(in: .newlines))")
}
@ -91,6 +91,7 @@ class ComposerWindow {
}
window = nil
removeBusyStatus()
menu = nil
completion(true)
}
}
@ -103,6 +104,7 @@ class ComposerWindow {
window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized
window = nil
removeBusyStatus()
menu = nil
completion(false)
}
}
@ -111,9 +113,7 @@ class ComposerWindow {
private func removeBusyStatus() {
PhpEnv.shared.isBusy = false
DispatchQueue.main.async { [self] in
menu?.updatePhpVersionInStatusBar()
}
menu?.updatePhpVersionInStatusBar()
}
// MARK: Alert
@ -128,4 +128,8 @@ class ComposerWindow {
.withPrimary(text: "OK")
.show()
}
deinit {
Log.perf("deinit: \(String(describing: self)).\(#function)")
}
}

View File

@ -29,6 +29,21 @@ class HomebrewDiagnostics {
return installedTaps.contains("nicoverbruggen/cask")
}()
/**
Determines whether to use the regular `nginx` or `nginx-full` formula.
*/
public static var usesNginxFullFormula: Bool = {
guard let destination = try? FileManager.default
.destinationOfSymbolicLink(atPath: "\(Paths.binPath)/nginx") else { return false }
return
// Verify that the `nginx` binary is symlinked to a directory that includes `nginx-full`.
destination.contains("/nginx-full/")
// Verify that the formula exists.
&& !Shell.pipe("\(Paths.brew) info nginx-full --json")
.contains("Error: No available formula")
}()
/**
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`).
@ -77,7 +92,7 @@ class HomebrewDiagnostics {
private static func hasAliasConflict() -> Bool {
let tapAlias = Shell.pipe("\(Paths.brew) info shivammathur/php/php --json")
if tapAlias.contains("brew tap shivammathur/php") || tapAlias.contains("Error") {
if tapAlias.contains("brew tap shivammathur/php") || tapAlias.contains("Error") || tapAlias.isEmpty {
Log.info("The user does not appear to have tapped: shivammathur/php")
return false
} else {
@ -128,10 +143,10 @@ class HomebrewDiagnostics {
}
/**
In order to see if we support the --json syntax, we'll query nginx.
In order to see if we support the --json syntax, we'll query dnsmasq.
If the JSON response cannot be parsed, Homebrew is probably out of date.
*/
public static func cannotLoadService(_ name: String = "nginx") -> Bool {
public static func cannotLoadService(_ name: String) -> Bool {
let serviceInfo = try? JSONDecoder().decode(
[HomebrewService].self,
from: Shell.pipe(

View File

@ -78,13 +78,19 @@ class Valet {
Notify the user about a non-default TLD being set.
*/
public static func notifyAboutUnsupportedTLD() {
if Valet.shared.config.tld != "test" {
if Valet.shared.config.tld != "test" && Preferences.isEnabled(.warnAboutNonStandardTLD) {
DispatchQueue.main.async {
BetterAlert().withInformation(
title: "alert.warnings.tld_issue.title".localized,
subtitle: "alert.warnings.tld_issue.subtitle".localized,
description: "alert.warnings.tld_issue.description".localized
).withPrimary(text: "OK").show()
)
.withPrimary(text: "OK")
.withTertiary(text: "alert.do_not_tell_again".localized, action: { alert in
Preferences.update(.warnAboutNonStandardTLD, value: false)
alert.close(with: .alertThirdButtonReturn)
})
.show()
}
}
}

View File

@ -29,12 +29,16 @@ 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."
)
Log.info(HomebrewDiagnostics.usesNginxFullFormula
? "The app will be using the `nginx-full` formula."
: "The app will be using the `nginx` formula."
)
// 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!)")
@ -155,11 +159,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)

View File

@ -212,18 +212,19 @@ extension StatusMenu {
func addXdebugMenuItem() {
if !Xdebug.enabled {
addItem(NSMenuItem.separator())
return
}
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)
}

View File

@ -48,8 +48,9 @@ class StatusMenu: NSMenu {
if Preferences.isEnabled(.displayExtensions) {
addExtensionsMenuItems()
addXdebugMenuItem()
NSMenuItem.separator()
addXdebugMenuItem()
}
addPhpDoctorMenuItem()

View File

@ -47,7 +47,7 @@ class BetterAlertVC: NSViewController {
}
deinit {
Log.perf("A BetterAlert has been deinitialized.")
Log.perf("deinit: \(String(describing: self)).\(#function)")
}
// MARK: Outlet Actions

View File

@ -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]?

View File

@ -26,6 +26,9 @@ enum PreferenceName: String {
case iconTypeToDisplay = "icon_type_to_display"
case fullPhpVersionDynamicIcon = "full_php_in_menu_bar"
// WARNINGS
case warnAboutNonStandardTLD = "warn_about_non_standard_tld"
// NOTIFICATIONS
case notifyAboutVersionChange = "notify_about_version_change"
case notifyAboutPhpFpmRestart = "notify_about_php_fpm_restart"
@ -60,6 +63,7 @@ enum PreferenceName: String {
.showPhpDoctorSuggestions,
// Notifications
.warnAboutNonStandardTLD,
.notifyAboutVersionChange,
.notifyAboutPhpFpmRestart,
.notifyAboutServices,

View File

@ -57,6 +57,7 @@ class Preferences {
PreferenceName.fullPhpVersionDynamicIcon.rawValue: false,
/// Preferences: Notifications
PreferenceName.warnAboutNonStandardTLD.rawValue: true,
PreferenceName.notifyAboutVersionChange.rawValue: true,
PreferenceName.notifyAboutPhpFpmRestart.rawValue: true,
PreferenceName.notifyAboutServices.rawValue: true,

View File

@ -25,7 +25,7 @@ class GenericPreferenceVC: NSViewController {
// MARK: - Deinitialization
deinit {
Log.perf("PrefsVC deallocated")
Log.perf("deinit: \(String(describing: self)).\(#function)")
}
func getDynamicIconPV() -> NSView {
@ -188,6 +188,16 @@ class GenericPreferenceVC: NSViewController {
)
}
func getWarnAboutNonStandardTLD() -> NSView {
return CheckboxPreferenceView.make(
sectionText: "prefs.warnings".localized,
descriptionText: "prefs.warn_about_non_standard_tld_desc".localized,
checkboxText: "prefs.warn_about_non_standard_tld".localized,
preference: .warnAboutNonStandardTLD,
action: {}
)
}
func getDisplayMenuSectionPV(
_ localizationKey: String,
_ preference: PreferenceName,
@ -249,7 +259,8 @@ class NotificationPreferencesVC: GenericPreferenceVC {
vc.getNotifyAboutSecureTogglePV(),
vc.getNotifyAboutGlobalComposerStatusPV(),
vc.getNotifyAboutServicesPV(),
vc.getNotifyAboutPhpFpmChangePV()
vc.getNotifyAboutPhpFpmChangePV(),
vc.getWarnAboutNonStandardTLD()
]
return vc

View File

@ -18,7 +18,7 @@ class ProgressViewController: NSViewController {
@IBOutlet weak var imageViewType: NSImageView!
deinit {
Log.perf("Deinitializing ProgressViewController")
Log.perf("deinit: \(String(describing: self)).\(#function)")
}
}

View File

@ -56,7 +56,7 @@ class TerminalProgressWindowController: NSWindowController, NSWindowDelegate {
}
deinit {
Log.perf("Deinitializing ProgressWindowController")
Log.perf("deinit: \(String(describing: self)).\(#function)")
}
}

View File

@ -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 {

View File

@ -17,9 +17,9 @@ struct ServicesView: View {
static func asMenuItem(perRow: Int = 3) -> NSMenuItem {
let item = NSMenuItem()
var services = [
PhpEnv.phpInstall.formula,
"nginx",
"dnsmasq"
Homebrew.Formulae.php,
Homebrew.Formulae.nginx,
Homebrew.Formulae.dnsmasq
]
if Preferences.custom.hasServices() {

View File

@ -68,7 +68,7 @@ class PhpConfigWatcher {
}
deinit {
Log.perf("A PhpConfigWatcher has been deinitialized.")
Log.perf("deinit: \(String(describing: self)).\(#function)")
}
}

View File

@ -227,6 +227,7 @@
"prefs.integrations" = "Integrations:";
"prefs.updates" = "Updates:";
"prefs.notifications" = "Notifications:";
"prefs.warnings" = "Warnings:";
"prefs.menu_contents" = "Features in Menu:";
"prefs.icon_options.php" = "Display PHP Icon";
@ -279,6 +280,9 @@
"prefs.notify_about_composer_success_desc" = "Displays a notification when the global Composer configuration was successfully updated.";
"prefs.notify_about_composer_success" = "Notify about global composer update";
"prefs.warn_about_non_standard_tld_desc" = "If you use a non-standard TLD, you may not wish to get repeated notifications about this.";
"prefs.warn_about_non_standard_tld" = "Warn about non-standard TLD";
"prefs.display_global_version_switcher_desc" = "If disabled, you will not be able to change the globally linked PHP version via the main menu.";
"prefs.display_global_version_switcher" = "PHP Switcher";
@ -504,7 +508,7 @@ If you are seeing this message but are confused why this folder has gone missing
/// Cannot retrieve services
"startup.errors.services_json_error.title" = "Cannot determine services status";
"startup.errors.services_json_error.subtitle" = "PHP Monitor usually queries `brew` using the following command to test if the services can be retrieved: `sudo brew services info nginx --json`.\n\nPHP Monitor could not interpret this response.";
"startup.errors.services_json_error.desc" = "This can happen if your Homebrew installation is out of date, in which case Homebrew won't return JSON yet. You can usually fix this by running `brew update`. You can also try running `sudo brew services info nginx --json` in your terminal of choice.";
"startup.errors.services_json_error.desc" = "This can happen if your Homebrew installation is out of date, in which case Homebrew won't return JSON yet. You can usually fix this by running `brew update` or `brew tap homebrew/services`. You can also try running `sudo brew services info nginx --json` in your terminal of choice.";
/// Issue with `which` alias
"startup.errors.which_alias_issue.title" = "A configuration issue was detected";
@ -548,6 +552,7 @@ If you are seeing this message but are confused why this folder has gone missing
"alert.warnings.tld_issue.title" = "You are not using `.test` as the TLD for Valet.";
"alert.warnings.tld_issue.subtitle" = "Using a non-default TLD may not work correctly and is not officially supported.";
"alert.warnings.tld_issue.description" = "PHP Monitor will remain functional, but there might be issues: the app might not correctly show which domains have been secured. For optimal results, go to your Valet configuration file (config.json in the Valet directory) and change the TLD back to `test`.";
"alert.do_not_tell_again" = "Don't Tell Me Again";
// WARNINGS