Skip to content

Commit

Permalink
Merge pull request #150 from root3nl/development
Browse files Browse the repository at this point in the history
v2.5.1
  • Loading branch information
jordywitteman authored Sep 29, 2023
2 parents abe1d66 + 4c5e730 commit d22d378
Show file tree
Hide file tree
Showing 12 changed files with 58 additions and 47 deletions.
15 changes: 0 additions & 15 deletions Jamf Pro Custom Schema/Jamf Pro Custom Schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,21 +105,6 @@
}
]
},
"HideMajorUpdates": {
"title": "Hide major macOS updates",
"description": "Ignore macOS major updates. This will prevent the menu bar icon and the macOS version info item from showing an available major update. Only applicable to macOS 12.3 and higher",
"type": "boolean",
"options": {
"enum_titles": ["Hide", "Show"],
"infoText": "Key name: HideMajorUpdates"
},
"links": [
{
"rel": "Documentation",
"href": "https://github.com/root3nl/SupportApp"
}
]
},
"UpdateText": {
"title": "Update Text",
"description": "Optional text to shown in the macOS Managed Updates popover to tell users about the updates",
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ The popover allows the user to open System Settings and install the update or up

<img src="/Screenshots/software_update_integration.png" width="500">

> **Note**
> When a deferral is set using the preference key `forceDelayedMajorSoftwareUpdates` in the domain `com.apple.applicationaccess`, major macOS updates will automatically be hidden indefinitely until the key is removed or set to `false`. The amount of days configured for the deferral are ignored. Due to limitations and complexity, it is not supported to automatically show the macOS major update once the deferral days are passed. This behaviour replaces the `HideMajorUpdates` key, previously available in version 2.5 and earlier. More info here: https://developer.apple.com/documentation/devicemanagement/restrictions
## Configuration
The configuration of the Support app is optimized for use with your MDM solution. The easiest way to configure the app is using a Configuration Profile so you can use whatever MDM solution you like, as long as it supports custom Configuration Profiles.

Expand All @@ -219,7 +222,6 @@ All general settings
| StatusBarIcon | String | Root3 Logo | Remote URL or path to the status bar icon shown in the menu bar. Recommended: PNG, 16x16 points. Icons larger than 22 points will automatically be resized to 16 points. A subfolder in `/Library/Application Support/` is the recommended location due to sandboxing | `/Library/Application Support/Your Company/statusbaricon.png` or `https://domain.tld/url_to_image.png` |
| StatusBarIconSFSymbol | String | Root3 Logo | Custom status bar icon using an SF Symbol. Ignored when StatusBarIcon is also set | “lifepreserver” |
| StatusBarIconNotifierEnabled | Boolean | false | Shows a small notification badge in the Status Bar Icon when an info items triggers a warning or notification | true |
| HideMajorUpdates | Boolean | false | Ignore macOS major updates. This will prevent the menu bar icon and the macOS version info item from showing an available major update. Only applicable to macOS 12.3 and higher | true |
| UpdateText | String | - | The text shown below the software update details popover | "Your organization requires you to update as soon as possible. [More info...](https://URL_TO_YOUR_UPDATE_POLICY)" |
| CustomColor | String | macOS Accent Color | Custom color for all symbols. Leave empty to use macOS Accent Color. We recommend not to use a very light color as text may become hard to read | HEX color in RGB format like "#8cc63f" |
| CustomColorDarkMode | String | macOS Accent Color | Custom color for all symbols in Dark Mode. Leave empty to use macOS Accent Color or CustomColor if specified. We recommend not to use a very dark color as text may become hard to read | HEX color in RGB format like "#8cc63f" |
Expand Down
12 changes: 11 additions & 1 deletion pkgbuild/scripts/postinstall
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,24 @@ install_location="/Applications/Support.app"
username=$(scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }')

# Remove "Downloaded from Internet" warning
xattr -d -r com.apple.quarantine "${install_location}"
# xattr -d -r com.apple.quarantine "${install_location}"

# Load Requirements
autoload is-at-least

# macOS Version
os_version=$(sw_vers -productVersion)

# ------------------ Gatekeeper scan ------------------

# Perform a Gatekeeper scan. This is useful for pre-warming the cache so users
# do not see the 'Verifying...' dialog on first launch of an application.
if is-at-least 14.0 ${os_version}; then
gktool scan "${install_location}"
fi

# ------------------ LaunchAgent ------------------

# Open the app so the legacy LaunchAgent properly displays the app name and icon
# in System Settings > General > Login Items
# Tested and does only work when a user is logged in during the installation
Expand Down
4 changes: 2 additions & 2 deletions src/Support.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 2.5;
MARKETING_VERSION = 2.5.1;
PRODUCT_BUNDLE_IDENTIFIER = nl.root3.support;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -598,7 +598,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 2.5;
MARKETING_VERSION = 2.5.1;
PRODUCT_BUNDLE_IDENTIFIER = nl.root3.support;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
19 changes: 11 additions & 8 deletions src/Support/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
// Make UserDefaults easy to use with suite "com.apple.SoftwareUpdate"
let ASUdefaults = UserDefaults(suiteName: "com.apple.SoftwareUpdate")

// Make UserDefaults easy to use with suite "com.apple.applicationaccess"
let restrictionsDefaults = UserDefaults(suiteName: "com.apple.applicationaccess")

// Make properties and preferences available
var computerinfo = ComputerInfo()
var userinfo = UserInfo()
Expand Down Expand Up @@ -108,6 +111,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
defaults.addObserver(self, forKeyPath: "StorageLimit", options: .new, context: nil)
defaults.addObserver(self, forKeyPath: "PasswordExpiryLimit", options: .new, context: nil)
defaults.addObserver(self, forKeyPath: "OpenAtLogin", options: .new, context: nil)
restrictionsDefaults?.addObserver(self, forKeyPath: "forceDelayedMajorSoftwareUpdates", options: .new, context: nil)
ASUdefaults?.addObserver(self, forKeyPath: "LastUpdatesAvailable", options: .new, context: nil)
ASUdefaults?.addObserver(self, forKeyPath: "RecommendedUpdates", options: .new, context: nil)

Expand Down Expand Up @@ -289,22 +293,18 @@ class AppDelegate: NSObject, NSApplicationDelegate {
preferences.infoItemSix
]

// Number of available software updates
var updatesAvailable = computerinfo.updatesAvailable

// If configured, ignore major macOS version updates
if preferences.hideMajorUpdates {
logger.debug("HideMajorUpdates is enabled, hiding \(self.computerinfo.majorVersionUpdates) major macOS updates")
updatesAvailable -= computerinfo.majorVersionUpdates
if computerinfo.forceDelayedMajorSoftwareUpdates {
logger.debug("forceDelayedMajorSoftwareUpdates is enabled, hiding \(self.computerinfo.majorVersionUpdates) major macOS updates")
}

// Show notification badge in menu bar icon when info item when needed
if (updatesAvailable == 0 || !infoItemsEnabled.contains("MacOSVersion")) && ((computerinfo.uptimeLimitReached && infoItemsEnabled.contains("Uptime")) || (computerinfo.selfSignedIP && infoItemsEnabled.contains("Network")) || (userinfo.passwordExpiryLimitReached && infoItemsEnabled.contains("Password")) || (computerinfo.storageLimitReached && infoItemsEnabled.contains("Storage")) || (preferences.extensionAlertA && infoItemsEnabled.contains("ExtensionA")) || (preferences.extensionAlertB && infoItemsEnabled.contains("ExtensionB"))) && defaults.bool(forKey: "StatusBarIconNotifierEnabled") {
if (computerinfo.updatesAvailableToShow == 0 || !infoItemsEnabled.contains("MacOSVersion")) && ((computerinfo.uptimeLimitReached && infoItemsEnabled.contains("Uptime")) || (computerinfo.selfSignedIP && infoItemsEnabled.contains("Network")) || (userinfo.passwordExpiryLimitReached && infoItemsEnabled.contains("Password")) || (computerinfo.storageLimitReached && infoItemsEnabled.contains("Storage")) || (preferences.extensionAlertA && infoItemsEnabled.contains("ExtensionA")) || (preferences.extensionAlertB && infoItemsEnabled.contains("ExtensionB"))) && defaults.bool(forKey: "StatusBarIconNotifierEnabled") {

// Create orange notification badge
orangeBadge.isHidden = false

} else if (updatesAvailable > 0 && infoItemsEnabled.contains("MacOSVersion")) && defaults.bool(forKey: "StatusBarIconNotifierEnabled") {
} else if (computerinfo.updatesAvailableToShow > 0 && infoItemsEnabled.contains("MacOSVersion")) && defaults.bool(forKey: "StatusBarIconNotifierEnabled") {

// Create red notification badge
redBadge.isHidden = false
Expand Down Expand Up @@ -408,6 +408,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
case "OpenAtLogin":
logger.debug("\(keyPath! as NSObject) changed to \(self.defaults.bool(forKey: "OpenAtLogin"), privacy: .public)")
self.configureLaunchAgent()
case "forceDelayedMajorSoftwareUpdates":
logger.debug("\(keyPath! as NSObject) changed to \(self.restrictionsDefaults!.bool(forKey: "forceDelayedMajorSoftwareUpdates"), privacy: .public)")
self.computerinfo.getRecommendedUpdates()
case "ExtensionAlertA":
logger.debug("\(keyPath! as NSObject) changed to \(self.preferences.extensionAlertA, privacy: .public)")
case "ExtensionAlertB":
Expand Down
28 changes: 25 additions & 3 deletions src/Support/ComputerInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ class ComputerInfo: ObservableObject {
// Number of major macOS Software Updates
@Published var majorVersionUpdates: Int = 0

// Calculate number of updates to show, excluding any major upgrades if hidden using 'forceDelayedMajorSoftwareUpdates' in a restrictions profile
var updatesAvailableToShow: Int {
if forceDelayedMajorSoftwareUpdates {
return updatesAvailable - majorVersionUpdates
} else {
return updatesAvailable
}
}

// Get if major OS updates are deferred using a restrictions profile
@AppStorage("forceDelayedMajorSoftwareUpdates", store: UserDefaults(suiteName: "com.apple.applicationaccess")) var forceDelayedMajorSoftwareUpdates: Bool = false

// Computer name
@Published var hostname = String()

Expand Down Expand Up @@ -325,7 +337,7 @@ class ComputerInfo: ObservableObject {

// Command to get c
let computerNameCommand = """
ioreg -l -c IOPlatformDevice | grep -e "product-name" | cut -d'"' -f 4
ioreg -c IOPlatformDevice | grep -e "product-name" | cut -d'"' -f 4
"""

// Move command to background thread
Expand Down Expand Up @@ -652,6 +664,12 @@ class ComputerInfo: ObservableObject {
// Return when decoded RecommendedUpdates array is empty
guard !decodedItems.isEmpty else {
self.logger.debug("RecommendedUpdates is empty...")

// Remove all updates from UI
DispatchQueue.main.async {
self.recommendedUpdates = []
}

return
}

Expand All @@ -661,16 +679,20 @@ class ComputerInfo: ObservableObject {
self.logger.debug("Updates found: \(decodedItems.count)")

// Loop through all available updates and decrease number of updates when available macOS version is higher than current major version
for item in decodedItems {
for (index, item) in decodedItems.enumerated() {
// Filter updates with "macOS" in Display Name
if item.displayName.contains("macOS") {
// Get digits from Display Version separated by a dot to get the major version
if let version = item.displayVersion?.components(separatedBy: ".")[0] {
self.logger.debug("macOS update found: \(item.displayName, privacy: .public)")
// Convert to integer and compare with current major OS version. If higher, increase number of major OS updates
if Int(version) ?? 0 > self.systemVersionMajor {
self.logger.debug("macOS version \(version, privacy: .public) is higher than the current macOS version (\(self.systemVersionMajor)), update will be hidden when DeferMajorVersions is enabled")
self.logger.debug("macOS version \(version, privacy: .public) is higher than the current macOS version (\(self.systemVersionMajor)), update will be hidden when forceDelayedMajorSoftwareUpdates is enabled")
majorVersionUpdatesTemp += 1
// Remove update item from array if forceDelayedMajorSoftwareUpdates is enabled
if self.forceDelayedMajorSoftwareUpdates && decodedItems.indices.contains(index) {
decodedItems.remove(at: index)
}
}
} else {
self.logger.error("Error getting macOS version from \(item.displayName, privacy: .public)")
Expand Down
2 changes: 1 addition & 1 deletion src/Support/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ extension String {
("$LocalMacosVersionName", computerInfo.macOSVersionName),
("$LocalSerialNumber", computerInfo.deviceSerialNumber),
("$LocalIpAddress", computerInfo.ipAddress),
("$LocalUpdatesAvailable", "\(computerInfo.updatesAvailable)"),
("$LocalUpdatesAvailable", "\(computerInfo.updatesAvailableToShow)"),
("\\n", "\n")
]

Expand Down
2 changes: 1 addition & 1 deletion src/Support/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>1690815745</string>
<string>1695998775</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
Expand Down
3 changes: 0 additions & 3 deletions src/Support/Preferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ class Preferences: ObservableObject {
// Automatically register modern LaunchAgent on macOS 13 and higher
@AppStorage("OpenAtLogin") var openAtLogin: Bool = false

// Hide major updates for macOS
@AppStorage("HideMajorUpdates") var hideMajorUpdates: Bool = false

// Optional text to show in the Managed Updates view
@AppStorage("UpdateText") var updateText: String = ""

Expand Down
1 change: 1 addition & 0 deletions src/Support/Support.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<true/>
<key>com.apple.security.temporary-exception.shared-preference.read-only</key>
<array>
<string>com.apple.applicationaccess</string>
<string>com.trusourcelabs.NoMAD</string>
<string>com.apple.SoftwareUpdate</string>
<string>com.jamf.connect.state</string>
Expand Down
13 changes: 2 additions & 11 deletions src/Support/Views/ButtonViews/MacOSVersionSubview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,14 @@ struct MacOSVersionSubview: View {
}
}

// Calculate number of updates to show
var updatesAvailable: Int {
if preferences.hideMajorUpdates {
return computerinfo.updatesAvailable - computerinfo.majorVersionUpdates
} else {
return computerinfo.updatesAvailable
}
}

var body: some View {

InfoItem(title: "macOS \(computerinfo.macOSVersionName)", subtitle: computerinfo.macOSVersion, image: "applelogo", symbolColor: Color(NSColor(hex: "\(customColor)") ?? NSColor.controlAccentColor), notificationBadge: updatesAvailable, hoverEffectEnable: true)
InfoItem(title: "macOS \(computerinfo.macOSVersionName)", subtitle: computerinfo.macOSVersion, image: "applelogo", symbolColor: Color(NSColor(hex: "\(customColor)") ?? NSColor.controlAccentColor), notificationBadge: computerinfo.updatesAvailableToShow, hoverEffectEnable: true)
.onTapGesture {
self.showUpdatePopover.toggle()
}
.popover(isPresented: $showUpdatePopover, arrowEdge: .leading) {
UpdateView(updateCounter: updatesAvailable, color: Color(NSColor(hex: "\(customColor)") ?? NSColor.controlAccentColor))
UpdateView(updateCounter: computerinfo.updatesAvailableToShow, color: Color(NSColor(hex: "\(customColor)") ?? NSColor.controlAccentColor))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Support/Views/UpdateView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct UpdateView: View {

VStack(alignment: .leading, spacing: 8) {

if updateCounter > 0 {
if computerinfo.recommendedUpdates.count > 0 {

HStack {

Expand Down

0 comments on commit d22d378

Please sign in to comment.