From d45f98db7ffbedc054b3727ea3af62f4f4c23f18 Mon Sep 17 00:00:00 2001
From: Andrew Barnett <138056529+AndrewMBarnett@users.noreply.github.com>
Date: Wed, 24 Jun 2026 16:10:55 -0500
Subject: [PATCH] Add deferral tracking to available updates EA and add last
update check time EA
Updated apps-with-available-updates (.sh and .xml) to include deferrals remaining per app, || continue error handling in the loop, a post-loop empty array guard, and printf instead of echo -e for output
Added new apps-last-update-check-time extension attribute (.sh and .xml) that returns the last time Alectrona Patch ran an update check, formatted as local time
---
.../apps-last-update-check-time.sh | 30 ++++++
.../apps-last-update-check-time.xml | 35 +++++++
.../apps-with-available-updates.sh | 22 +++--
.../apps-with-available-updates.xml | 94 ++++++++++---------
4 files changed, 131 insertions(+), 50 deletions(-)
create mode 100644 Extension-Attributes/apps-last-update-check-time.sh
create mode 100644 Extension-Attributes/apps-last-update-check-time.xml
diff --git a/Extension-Attributes/apps-last-update-check-time.sh b/Extension-Attributes/apps-last-update-check-time.sh
new file mode 100644
index 0000000..6366410
--- /dev/null
+++ b/Extension-Attributes/apps-last-update-check-time.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# Last Update Check
+#
+# This attribute returns the last time Alectrona Patch ran an update check.
+#
+# Exit if Alectrona Patch is not installed
+if [[ ! -e /Library/Application\ Support/Alectrona/Patch/patch ]]; then
+ exit 1
+fi
+
+# Generate the report
+report=$(/Library/Application\ Support/Alectrona/Patch/patch report --updatable 2>/dev/null)
+if [[ -z "$report" ]]; then
+ echo "No report available"
+ exit 0
+fi
+
+# Extract the last update check timestamp
+lastCheck=$(echo "$report" | /usr/bin/plutil -extract patchInfo.lastUpdateTaskRun raw -o - -) || {
+ echo "Unknown"
+ exit 0
+}
+
+epoch=$(date -j -u -f "%Y-%m-%dT%H:%M:%SZ" "$lastCheck" "+%s")
+localTime=$(date -r "$epoch" "+%Y-%m-%d %H:%M:%S %Z")
+
+echo "$localTime"
+
+exit 0
diff --git a/Extension-Attributes/apps-last-update-check-time.xml b/Extension-Attributes/apps-last-update-check-time.xml
new file mode 100644
index 0000000..60bb513
--- /dev/null
+++ b/Extension-Attributes/apps-last-update-check-time.xml
@@ -0,0 +1,35 @@
+
+Apps Last Update Check Time
+This attribute returns the last time Alectrona Patch ran an update check.
+string
+#!/bin/bash
+
+# Last Update Check
+#
+# This attribute returns the last time Alectrona Patch ran an update check.
+#
+# Exit if Alectrona Patch is not installed
+if [[ ! -e /Library/Application\ Support/Alectrona/Patch/patch ]]; then
+ exit 1
+fi
+
+# Generate the report
+report=$(/Library/Application\ Support/Alectrona/Patch/patch report --updatable 2>/dev/null)
+if [[ -z "$report" ]]; then
+ echo "<result>No report available</result>"
+ exit 0
+fi
+
+# Extract the last update check timestamp
+lastCheck=$(echo "$report" | /usr/bin/plutil -extract patchInfo.lastUpdateTaskRun raw -o - -) || {
+ echo "<result>Unknown</result>"
+ exit 0
+}
+
+epoch=$(date -j -u -f "%Y-%m-%dT%H:%M:%SZ" "$lastCheck" "+%s")
+localTime=$(date -r "$epoch" "+%Y-%m-%d %H:%M:%S %Z")
+
+echo "<result>$localTime</result>"
+
+exit 0
+
diff --git a/Extension-Attributes/apps-with-available-updates.sh b/Extension-Attributes/apps-with-available-updates.sh
index 0a066d0..6757b8c 100644
--- a/Extension-Attributes/apps-with-available-updates.sh
+++ b/Extension-Attributes/apps-with-available-updates.sh
@@ -1,9 +1,9 @@
#!/bin/bash
# Apps With Available Updates
-
+#
# This attribute returns all installed apps that are updatable (out-of-date) using Alectrona Patch.
-
+#
# Exit if Alectrona Patch is not installed
if [[ ! -e /Library/Application\ Support/Alectrona/Patch/patch ]]; then
exit 1
@@ -27,18 +27,26 @@ fi
# Loop through the appInventory array and build the result
for ((index=0; index/dev/null) || deferralCount=0
+ deferralsRemaining=$(( deferralLimit - deferralCount ))
# Add the app to the array
- updatableApps+=("ID: $appID\nInstalled Version: $installedVersion\nAvailable Version: $targetVersion\n")
+ updatableApps+=("ID: $appID"$'\n'"Installed Version: $installedVersion"$'\n'"Available Version: $targetVersion"$'\n'"Deferrals Remaining: $deferralsRemaining"$'\n')
done
# Join array elements with newlines
+if [[ "${#updatableApps[@]}" -eq 0 ]]; then
+ echo "No updatable apps"
+ exit 0
+fi
+
result=$(printf "%s\n" "${updatableApps[@]}")
# Return the result
-echo -e "$result"
+printf "%b\n" "$result"
exit 0
\ No newline at end of file
diff --git a/Extension-Attributes/apps-with-available-updates.xml b/Extension-Attributes/apps-with-available-updates.xml
index 42c4cf6..f366ec6 100644
--- a/Extension-Attributes/apps-with-available-updates.xml
+++ b/Extension-Attributes/apps-with-available-updates.xml
@@ -2,48 +2,56 @@
Apps With Available Updates
This attribute returns all installed apps that are updatable (out-of-date) using Alectrona Patch.
string
-#!/bin/bash
-
-# Apps With Available Updates
-
-# This attribute returns all installed apps that are updatable (out-of-date) using Alectrona Patch.
-
-# Exit if Alectrona Patch is not installed
-if [[ ! -e /Library/Application\ Support/Alectrona/Patch/patch ]]; then
- exit 1
-fi
-
-# Generate the report
-report=$(/Library/Application\ Support/Alectrona/Patch/patch report --updatable 2>/dev/null)
-if [[ -z "$report" ]]; then
- echo "<result>No updatable apps</result>"
- exit 0
-fi
-
-# Get the length of the appInventory array
-length=$(echo "$report" | /usr/bin/plutil -extract appInventory raw -o - -)
-updatableApps=()
-
-if [[ "$length" -eq 0 ]]; then
- echo "<result>No updatable apps</result>"
- exit 0
-fi
-
-# Loop through the appInventory array and build the result
-for ((index=0; index<length; index++)); do
- appID=$(echo "$report" | /usr/bin/plutil -extract appInventory.$index.id raw -o - -)
- installedVersion=$(echo "$report" | /usr/bin/plutil -extract appInventory.$index.version raw -o - -)
- targetVersion=$(echo "$report" | /usr/bin/plutil -extract appInventory.$index.targetVersion raw -o - -)
-
- # Add the app to the array
- updatableApps+=("ID: $appID\nInstalled Version: $installedVersion\nAvailable Version: $targetVersion\n")
-done
-
-# Join array elements with newlines
-result=$(printf "%s\n" "${updatableApps[@]}")
-
-# Return the result
-echo -e "<result>$result</result>"
-
+#!/bin/bash
+
+# Apps With Available Updates
+#
+# This attribute returns all installed apps that are updatable (out-of-date) using Alectrona Patch.
+#
+# Exit if Alectrona Patch is not installed
+if [[ ! -e /Library/Application\ Support/Alectrona/Patch/patch ]]; then
+ exit 1
+fi
+
+# Generate the report
+report=$(/Library/Application\ Support/Alectrona/Patch/patch report --updatable 2>/dev/null)
+if [[ -z "$report" ]]; then
+ echo "<result>No updatable apps</result>"
+ exit 0
+fi
+
+# Get the length of the appInventory array
+length=$(echo "$report" | /usr/bin/plutil -extract appInventory raw -o - -)
+updatableApps=()
+
+if [[ "$length" -eq 0 ]]; then
+ echo "<result>No updatable apps</result>"
+ exit 0
+fi
+
+# Loop through the appInventory array and build the result
+for ((index=0; index<length; index++)); do
+ appID=$(echo "$report" | /usr/bin/plutil -extract appInventory.$index.id raw -o - -) || continue
+ installedVersion=$(echo "$report" | /usr/bin/plutil -extract appInventory.$index.version raw -o - -) || continue
+ targetVersion=$(echo "$report" | /usr/bin/plutil -extract appInventory.$index.targetVersion raw -o - -) || continue
+ deferralLimit=$(echo "$report" | /usr/bin/plutil -extract appInventory.$index.deferralLimit raw -o - -) || continue
+ deferralCount=$(echo "$report" | /usr/bin/plutil -extract appInventory.$index.deferralCount raw -o - - 2>/dev/null) || deferralCount=0
+ deferralsRemaining=$(( deferralLimit - deferralCount ))
+
+ # Add the app to the array
+ updatableApps+=("ID: $appID"$'\n'"Installed Version: $installedVersion"$'\n'"Available Version: $targetVersion"$'\n'"Deferrals Remaining: $deferralsRemaining"$'\n')
+done
+
+# Join array elements with newlines
+if [[ "${#updatableApps[@]}" -eq 0 ]]; then
+ echo "<result>No updatable apps</result>"
+ exit 0
+fi
+
+result=$(printf "%s\n" "${updatableApps[@]}")
+
+# Return the result
+printf "<result>%b</result>\n" "$result"
+
exit 0