From 8811b43e4480676f3d30e016b0e8d48fe7e8b1d1 Mon Sep 17 00:00:00 2001 From: Graham Gilbert Date: Thu, 13 Aug 2026 08:09:23 -0700 Subject: [PATCH] Fix Munki 7 log parsing in status display Munki 7 changed its log timestamp format from a month-name prefix ("Aug 13 2026 02:11:46 -0700") to ISO 8601 with milliseconds and a colon timezone ("2026-08-12 07:22:31.771-07:00"). The status parser stripped a fixed 27 characters off each line to remove the timestamp, which was calibrated to the old format; against Munki 7 lines it left "00 " from the "-07:00" offset bleeding into the displayed status. Replace the fixed-width strip with a regex that matches both the legacy and Munki 7 timestamp formats, and fold the two identical strip blocks (the -munki and -airwatch cases) into a shared helper. Add unit tests covering both formats. Co-Authored-By: Claude Opus 4.8 (1M context) --- DEPNotify/TrackProgress.swift | 44 ++++++++++++++--------------- DEPNotifyTests/DEPNotifyTests.swift | 15 ++++++++-- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/DEPNotify/TrackProgress.swift b/DEPNotify/TrackProgress.swift index b5bac08..f723ce8 100755 --- a/DEPNotify/TrackProgress.swift +++ b/DEPNotify/TrackProgress.swift @@ -327,32 +327,12 @@ class TrackProgress: NSObject { case OtherLogs.munki : if (line.contains("Installing") || line.contains("Downloading")) && !line.contains(" at ") && !line.contains(" from ") { - - do { - let installerRegEx = try NSRegularExpression(pattern: "^.{0,27}") - let status = installerRegEx.stringByReplacingMatches(in: line, - options: NSRegularExpression.MatchingOptions.anchored, - range: NSMakeRange(0, line.count), - withTemplate: "").trimmingCharacters(in: .whitespacesAndNewlines) - statusText = status - } catch { - NSLog("Couldn't parse ManagedSoftwareUpdate.log") - } + statusText = stripMunkiTimestamp(line) } case OtherLogs.airwatch : if (line.contains("Installing") || line.contains("Downloading") || line.contains("Install of")) && !line.contains(" at ") && !line.contains(" from ") { - - do { - let installerRegEx = try NSRegularExpression(pattern: "^.{0,27}") - let status = installerRegEx.stringByReplacingMatches(in: line, - options: NSRegularExpression.MatchingOptions.anchored, - range: NSMakeRange(0, line.count), - withTemplate: "").trimmingCharacters(in: .whitespacesAndNewlines) - statusText = status - } catch { - NSLog("Couldn't parse ManagedSoftwareUpdate.log") - } + statusText = stripMunkiTimestamp(line) } case OtherLogs.none : break @@ -364,6 +344,26 @@ class TrackProgress: NSObject { } } + // Strips the leading timestamp from a Munki/AirWatch log line, returning the + // status message. Handles both the pre-7 month-name format + // ("Aug 13 2026 02:11:46 -0700 ") and the Munki 7 ISO format with + // milliseconds and colon timezone ("2026-08-12 07:22:31.771-07:00 "). + func stripMunkiTimestamp(_ line: String) -> String { + let legacy = "[A-Za-z]{3} \\d{1,2} \\d{4} \\d{2}:\\d{2}:\\d{2} [-+]\\d{4}" + let iso = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(\\.\\d+)?\\s*[-+]\\d{2}:?\\d{2}" + let pattern = "^(\(legacy)|\(iso))\\s+" + do { + let regex = try NSRegularExpression(pattern: pattern) + return regex.stringByReplacingMatches(in: line, + options: NSRegularExpression.MatchingOptions.anchored, + range: NSMakeRange(0, line.count), + withTemplate: "").trimmingCharacters(in: .whitespacesAndNewlines) + } catch { + NSLog("Couldn't parse ManagedSoftwareUpdate.log") + return line.trimmingCharacters(in: .whitespacesAndNewlines) + } + } + func killCommandFile() { // delete the command file diff --git a/DEPNotifyTests/DEPNotifyTests.swift b/DEPNotifyTests/DEPNotifyTests.swift index ef88eb6..175d64f 100755 --- a/DEPNotifyTests/DEPNotifyTests.swift +++ b/DEPNotifyTests/DEPNotifyTests.swift @@ -21,9 +21,18 @@ class DEPNotifyTests: XCTestCase { super.tearDown() } - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. + func testStripMunkiTimestampMunki7() { + // Munki 7 format: milliseconds + colon timezone, no space before tz. + let tp = TrackProgress() + let line = "2026-08-12 07:22:31.771-07:00 Downloading GoogleChrome-1.5.pkg..." + XCTAssertEqual(tp.stripMunkiTimestamp(line), "Downloading GoogleChrome-1.5.pkg...") + } + + func testStripMunkiTimestampLegacy() { + // Pre-7 format: month-name date, space before tz. + let tp = TrackProgress() + let line = "Aug 13 2026 02:11:46 -0700 Installing Foo" + XCTAssertEqual(tp.stripMunkiTimestamp(line), "Installing Foo") } func testPerformanceExample() {