Skip to content

Commit 675fabf

Browse files
committed
Ship 14.0.5 helper recovery release
1 parent 49e36e2 commit 675fabf

23 files changed

Lines changed: 546 additions & 62 deletions

.github/workflows/release.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,32 @@ jobs:
7070
security list-keychains -d user -s "${KEYCHAIN_PATH}" $(security list-keychains -d user | tr -d '"')
7171
security set-key-partition-list -S apple-tool:,apple: -s -k "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}"
7272
73+
- name: Install WeatherKit provisioning profile
74+
env:
75+
WEATHERKIT_PROVISIONING_PROFILE_BASE64: ${{ secrets.WEATHERKIT_PROVISIONING_PROFILE_BASE64 }}
76+
run: |
77+
if [[ -z "${WEATHERKIT_PROVISIONING_PROFILE_BASE64}" ]]; then
78+
echo "Missing signing secret. Configure WEATHERKIT_PROVISIONING_PROFILE_BASE64." >&2
79+
exit 1
80+
fi
81+
82+
PROFILE_DIR="$HOME/Library/MobileDevice/Provisioning Profiles"
83+
PROFILE_PATH="$RUNNER_TEMP/core-monitor-weatherkit.provisionprofile"
84+
PROFILE_PLIST="$RUNNER_TEMP/core-monitor-weatherkit.plist"
85+
86+
mkdir -p "${PROFILE_DIR}"
87+
echo -n "${WEATHERKIT_PROVISIONING_PROFILE_BASE64}" | base64 -D > "${PROFILE_PATH}"
88+
security cms -D -i "${PROFILE_PATH}" > "${PROFILE_PLIST}"
89+
90+
PROFILE_UUID=$(/usr/libexec/PlistBuddy -c "Print UUID" "${PROFILE_PLIST}")
91+
PROFILE_NAME=$(/usr/libexec/PlistBuddy -c "Print Name" "${PROFILE_PLIST}")
92+
93+
cp "${PROFILE_PATH}" "${PROFILE_DIR}/${PROFILE_UUID}.provisionprofile"
94+
echo "RELEASE_PROVISIONING_PROFILE_SPECIFIER=${PROFILE_NAME}" >> "${GITHUB_ENV}"
95+
7396
- name: Build release archive
97+
env:
98+
RELEASE_PROVISIONING_PROFILE_SPECIFIER: ${{ env.RELEASE_PROVISIONING_PROFILE_SPECIFIER }}
7499
run: ./scripts/release/build_release.sh
75100

76101
- name: Notarize and staple
@@ -79,6 +104,9 @@ jobs:
79104
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
80105
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
81106
NOTARYTOOL_PROFILE: ${{ secrets.NOTARYTOOL_PROFILE }}
107+
APP_STORE_CONNECT_API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }}
108+
APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
109+
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
82110
run: ./scripts/release/notarize_release.sh build/release/Core-Monitor.app.zip build/release/export/Core-Monitor.app
83111

84112
- name: Collect release metadata

App-Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<key>SMPrivilegedExecutables</key>
3030
<dict>
3131
<key>ventaphobia.smc-helper</key>
32-
<string>identifier "ventaphobia.smc-helper" and anchor apple generic and certificate leaf[subject.OU] = "6VDP675K4L"</string>
32+
<string>anchor apple generic and identifier "ventaphobia.smc-helper" and certificate leaf[subject.OU] = "6VDP675K4L"</string>
3333
</dict>
3434
</dict>
3535
</plist>

Core-Monitor.xcodeproj/project.pbxproj

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@
263263
inputFileListPaths = (
264264
);
265265
inputPaths = (
266-
"$(BUILT_PRODUCTS_DIR)/smc-helper",
266+
"$(BUILT_PRODUCTS_DIR)/$(PRIVILEGED_HELPER_LABEL)",
267267
);
268268
name = "Embed Privileged Helper";
269269
outputFileListPaths = (
@@ -273,7 +273,7 @@
273273
);
274274
runOnlyForDeploymentPostprocessing = 0;
275275
shellPath = /bin/sh;
276-
shellScript = "set -e\nHELPER_SRC=${BUILT_PRODUCTS_DIR}/smc-helper\nHELPER_DST=${TARGET_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Library/LaunchServices/${PRIVILEGED_HELPER_LABEL}\nmkdir -p $(dirname $HELPER_DST)\ncp -f $HELPER_SRC $HELPER_DST\n";
276+
shellScript = "set -e\nHELPER_SRC=${BUILT_PRODUCTS_DIR}/${PRIVILEGED_HELPER_LABEL}\nHELPER_DST=${TARGET_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Library/LaunchServices/${PRIVILEGED_HELPER_LABEL}\nmkdir -p $(dirname $HELPER_DST)\ncp -f $HELPER_SRC $HELPER_DST\n";
277277
};
278278
/* End PBXShellScriptBuildPhase section */
279279

@@ -456,7 +456,7 @@
456456
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
457457
CODE_SIGN_STYLE = Automatic;
458458
COMBINE_HIDPI_IMAGES = YES;
459-
CURRENT_PROJECT_VERSION = 14040;
459+
CURRENT_PROJECT_VERSION = 14050;
460460
DEAD_CODE_STRIPPING = YES;
461461
DEVELOPMENT_TEAM = 6VDP675K4L;
462462
ENABLE_APP_SANDBOX = NO;
@@ -473,7 +473,7 @@
473473
"@executable_path/../Frameworks",
474474
);
475475
LIBRARY_SEARCH_PATHS = "$(inherited)";
476-
MARKETING_VERSION = 14.0.4;
476+
MARKETING_VERSION = 14.0.5;
477477
OTHER_LDFLAGS = "";
478478
PRODUCT_BUNDLE_IDENTIFIER = "CoreTools.Core-Monitor";
479479
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -502,10 +502,10 @@
502502
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
503503
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
504504
AUTOMATION_APPLE_EVENTS = NO;
505-
CODE_SIGN_ENTITLEMENTS = "Core-Monitor.entitlements";
505+
CODE_SIGN_ENTITLEMENTS = "Core-Monitor-WeatherKit.entitlements";
506506
CODE_SIGN_STYLE = Automatic;
507507
COMBINE_HIDPI_IMAGES = YES;
508-
CURRENT_PROJECT_VERSION = 14040;
508+
CURRENT_PROJECT_VERSION = 14050;
509509
DEAD_CODE_STRIPPING = YES;
510510
DEVELOPMENT_TEAM = 6VDP675K4L;
511511
ENABLE_APP_SANDBOX = NO;
@@ -522,7 +522,7 @@
522522
"@executable_path/../Frameworks",
523523
);
524524
LIBRARY_SEARCH_PATHS = "$(inherited)";
525-
MARKETING_VERSION = 14.0.4;
525+
MARKETING_VERSION = 14.0.5;
526526
OTHER_LDFLAGS = "";
527527
PRODUCT_BUNDLE_IDENTIFIER = "$(CORE_MONITOR_APP_BUNDLE_IDENTIFIER)";
528528
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -563,7 +563,7 @@
563563
"$(inherited)\n-sectcreate\n__TEXT\n__info_plist\n$(SRCROOT)/$(INFOPLIST_FILE)\n-sectcreate\n__TEXT\n__launchd_plist\n$(SRCROOT)/smc-helper/Launchd.plist",
564564
);
565565
PRODUCT_BUNDLE_IDENTIFIER = "$(PRIVILEGED_HELPER_LABEL)";
566-
PRODUCT_NAME = "$(TARGET_NAME)";
566+
PRODUCT_NAME = "$(PRIVILEGED_HELPER_LABEL)";
567567
SKIP_INSTALL = YES;
568568
SWIFT_APPROACHABLE_CONCURRENCY = YES;
569569
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
@@ -589,7 +589,7 @@
589589
"$(inherited)\n-sectcreate\n__TEXT\n__info_plist\n$(SRCROOT)/$(INFOPLIST_FILE)\n-sectcreate\n__TEXT\n__launchd_plist\n$(SRCROOT)/smc-helper/Launchd.plist",
590590
);
591591
PRODUCT_BUNDLE_IDENTIFIER = "$(PRIVILEGED_HELPER_LABEL)";
592-
PRODUCT_NAME = "$(TARGET_NAME)";
592+
PRODUCT_NAME = "$(PRIVILEGED_HELPER_LABEL)";
593593
SKIP_INSTALL = YES;
594594
SWIFT_APPROACHABLE_CONCURRENCY = YES;
595595
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;

Core-Monitor/ContentView.swift

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -894,8 +894,8 @@ private struct FanHelperStatusCard: View {
894894
.background(statusColor.opacity(0.12))
895895
.clipShape(Capsule())
896896
} else {
897-
Button("Install Helper") {
898-
helperManager.installFromApp()
897+
Button(helperButtonTitle) {
898+
helperManager.installFromApp(forceReinstall: helperManager.connectionState == .unreachable)
899899
}
900900
.buttonStyle(.borderedProminent)
901901
.controlSize(.small)
@@ -923,16 +923,19 @@ private struct FanHelperStatusCard: View {
923923
}
924924

925925
private var helperDescription: String {
926+
if helperManager.connectionState == .unreachable {
927+
return helperManager.isInstalled
928+
? "Installed, but the XPC service rejected or could not reach this app build. Reinstall the helper from this exact app build, then recheck."
929+
: "Core Monitor found an incomplete or stale helper install state. Repair the helper from this exact app build before switching into Smart, Manual, or other managed fan profiles."
930+
}
931+
926932
if helperIsRequiredNow == false {
927933
if helperManager.connectionState == .reachable {
928934
return "The helper is ready if you switch back into a managed fan mode later. System-owned cooling is active right now."
929935
}
930936
return "System mode keeps the firmware fan curve in charge right now. Install or repair the helper before switching into Smart, Manual, or other managed fan profiles."
931937
}
932938

933-
if helperManager.connectionState == .unreachable {
934-
return "Installed, but the XPC service rejected or could not reach this app build. Reinstall the helper from this exact app build, then recheck."
935-
}
936939
if helperManager.connectionState == .checking {
937940
return "Installed. Verifying the local privileged helper before enabling managed fan modes."
938941
}
@@ -948,6 +951,10 @@ private struct FanHelperStatusCard: View {
948951
}
949952
return helperIsRequiredNow ? .orange : .secondary
950953
}
954+
955+
private var helperButtonTitle: String {
956+
helperManager.connectionState == .unreachable ? "Repair Helper" : "Install Helper"
957+
}
951958
}
952959

953960
private struct HelperDiagnosticsSupportCard: View {
@@ -1068,7 +1075,9 @@ private struct HelperDiagnosticsSupportCard: View {
10681075
case .checking:
10691076
return "Core Monitor is verifying the local helper before trusting it for fan writes."
10701077
case .unreachable:
1071-
return "The helper is installed but this build could not establish a trusted XPC connection. Reinstall from this exact app build, then recheck if needed."
1078+
return helperManager.isInstalled
1079+
? "The helper is installed but this build could not establish a trusted XPC connection. Reinstall from this exact app build, then recheck if needed."
1080+
: "Core Monitor detected an incomplete or stale helper install. Repair the helper from this exact app build, then recheck if needed."
10721081
case .unknown:
10731082
return helperManager.isInstalled
10741083
? "The helper exists, but Core Monitor has not finished a fresh health probe yet."
@@ -1080,13 +1089,14 @@ private struct HelperDiagnosticsSupportCard: View {
10801089

10811090
private func performPrimaryAction() {
10821091
exportMessage = nil
1092+
if helperManager.connectionState == .unreachable {
1093+
helperManager.installFromApp(forceReinstall: true)
1094+
return
1095+
}
1096+
10831097
if helperManager.isInstalled {
1084-
if helperManager.connectionState == .unreachable {
1085-
helperManager.installFromApp(forceReinstall: true)
1086-
} else {
1087-
helperManager.refreshStatus()
1088-
helperManager.refreshDiagnostics()
1089-
}
1098+
helperManager.refreshStatus()
1099+
helperManager.refreshDiagnostics()
10901100
} else {
10911101
helperManager.installFromApp()
10921102
}

Core-Monitor/FanController.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,10 @@ enum CustomPresetSaveOutcome {
375375
final class FanController: ObservableObject {
376376
static let defaultMode: FanControlMode = .automatic
377377

378+
static func shouldRequestSystemAutomaticHandoff(lastAppliedSpeed: Int) -> Bool {
379+
lastAppliedSpeed > 0
380+
}
381+
378382
@Published var mode: FanControlMode = FanController.defaultMode
379383
@Published var manualSpeed: Int = 2200
380384
@Published var autoAggressiveness: Double = 1.5
@@ -706,8 +710,12 @@ final class FanController: ObservableObject {
706710

707711
switch mode {
708712
case .automatic, .silent:
709-
resetToSystemAutomatic()
710-
statusMessage = "System automatic control restored"
713+
if Self.shouldRequestSystemAutomaticHandoff(lastAppliedSpeed: lastAppliedSpeed) {
714+
resetToSystemAutomatic()
715+
} else {
716+
statusMessage = passiveStatusMessage(for: mode)
717+
}
718+
lastAppliedSpeed = -1
711719
case .manual:
712720
applyFanSpeed(manualSpeed)
713721
lastAppliedSpeed = manualSpeed
@@ -730,11 +738,12 @@ final class FanController: ObservableObject {
730738
statusMessage = "Manual: \(manualSpeed) RPM"
731739

732740
case .automatic, .silent:
733-
if lastAppliedSpeed != -1 {
741+
if Self.shouldRequestSystemAutomaticHandoff(lastAppliedSpeed: lastAppliedSpeed) {
734742
resetToSystemAutomatic()
743+
} else {
744+
statusMessage = "System automatic mode is active"
735745
}
736746
lastAppliedSpeed = -1
737-
statusMessage = "System automatic mode is active"
738747

739748
case .balanced:
740749
applyFixedPercentProfile(0.60, label: "Balanced")

Core-Monitor/HelperDiagnosticsExporter.swift

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ struct HelperDiagnosticsContext: Equatable {
3434
let installedHelperExists: Bool
3535
let connectionState: HelperDiagnosticsConnectionState
3636
let helperStatusMessage: String?
37+
let fanBackendRepository: String
38+
let fanModeKeyFormat: String?
39+
let fanForceTestAvailable: Bool?
3740
let launchAtLoginEnabled: Bool
3841
let launchAtLoginError: String?
3942
let enabledMenuBarItemCount: Int
@@ -63,6 +66,9 @@ struct HelperDiagnosticsReport: Codable, Equatable {
6366
let installedHelperExists: Bool
6467
let connectionState: HelperDiagnosticsConnectionState
6568
let statusMessage: String?
69+
let backendRepository: String
70+
let modeKeyFormat: String?
71+
let forceTestAvailable: Bool?
6672
}
6773

6874
struct LaunchAtLoginDetails: Codable, Equatable {
@@ -85,6 +91,8 @@ struct HelperDiagnosticsReport: Codable, Equatable {
8591
}
8692

8793
enum HelperDiagnosticsExporter {
94+
private static let fanBackendRepository = "agoodkind/macos-smc-fan"
95+
8896
@MainActor
8997
static func exportReport(
9098
helperManager: SMCHelperManager,
@@ -131,6 +139,7 @@ enum HelperDiagnosticsExporter {
131139
let installedHelperPath = "/Library/PrivilegedHelperTools/\(helperLabel)"
132140
let fileManager = FileManager.default
133141
let hostModelIdentifier = SystemMonitor.hostModelIdentifier()
142+
let controlMetadata = helperManager.readControlMetadata()
134143

135144
return HelperDiagnosticsContext(
136145
generatedAt: Date(),
@@ -148,6 +157,9 @@ enum HelperDiagnosticsExporter {
148157
installedHelperExists: fileManager.fileExists(atPath: installedHelperPath),
149158
connectionState: map(helperManager.connectionState),
150159
helperStatusMessage: helperManager.statusMessage,
160+
fanBackendRepository: fanBackendRepository,
161+
fanModeKeyFormat: controlMetadata?.modeKeyFormat,
162+
fanForceTestAvailable: controlMetadata?.forceTestAvailable,
151163
launchAtLoginEnabled: startupManager.isEnabled,
152164
launchAtLoginError: startupManager.errorMessage,
153165
enabledMenuBarItemCount: menuBarSettings.enabledItemCount,
@@ -180,7 +192,10 @@ enum HelperDiagnosticsExporter {
180192
installedHelperPath: context.installedHelperPath,
181193
installedHelperExists: context.installedHelperExists,
182194
connectionState: context.connectionState,
183-
statusMessage: context.helperStatusMessage
195+
statusMessage: context.helperStatusMessage,
196+
backendRepository: context.fanBackendRepository,
197+
modeKeyFormat: context.fanModeKeyFormat,
198+
forceTestAvailable: context.fanForceTestAvailable
184199
),
185200
launchAtLogin: .init(
186201
enabled: context.launchAtLoginEnabled,
@@ -200,7 +215,9 @@ enum HelperDiagnosticsExporter {
200215
case .checking, .unknown:
201216
return "Helper exists, but Core Monitor has not completed a trusted health probe yet."
202217
case .unreachable:
203-
return "Helper is installed, but this app could not establish a trusted connection to it."
218+
return context.installedHelperExists
219+
? "Helper is installed, but this app could not establish a trusted connection to it."
220+
: "Helper install is incomplete or stale, so this app cannot trust it yet."
204221
case .missing:
205222
return "Monitoring-only configuration. The privileged helper is not installed."
206223
}
@@ -217,6 +234,8 @@ enum HelperDiagnosticsExporter {
217234
case .unreachable:
218235
if let issue = context.signingInfo.issue, issue.isEmpty == false {
219236
actions.append(issue)
237+
} else if context.installedHelperExists == false {
238+
actions.append("Repair the privileged helper from this exact app build so the stale or incomplete install can be replaced cleanly.")
220239
} else {
221240
actions.append("Reinstall the privileged helper from this exact app build, then recheck connectivity before trusting fan control.")
222241
}

Core-Monitor/MenuBarStatusSummary.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ enum MenuBarStatusSummary {
3131
isInstalled: Bool
3232
) -> MenuBarStatusPillSummary {
3333
guard mode.requiresPrivilegedHelper else {
34+
if connectionState == .unreachable {
35+
return MenuBarStatusPillSummary(label: "Helper Attention", tone: .critical)
36+
}
3437
return MenuBarStatusPillSummary(label: "Helper Optional", tone: .neutral)
3538
}
3639

Core-Monitor/MonitoringDashboardViews.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,14 @@ struct MonitoringDashboardStrip: View {
8888
}
8989

9090
private var helperDetail: String {
91-
if fanController.mode.requiresPrivilegedHelper == false {
92-
return "Current cooling mode does not require the helper."
93-
}
94-
9591
if let statusMessage = helperManager.statusMessage, statusMessage.isEmpty == false {
9692
return statusMessage
9793
}
9894

95+
if fanController.mode.requiresPrivilegedHelper == false {
96+
return "Current cooling mode does not require the helper."
97+
}
98+
9999
switch helperManager.connectionState {
100100
case .reachable:
101101
return "The privileged helper is installed and responding."
@@ -247,14 +247,14 @@ struct SystemStatusBoard: View {
247247
}
248248

249249
private var helperDetail: String {
250-
if fanController.mode.requiresPrivilegedHelper == false {
251-
return "Current cooling mode does not require the helper."
252-
}
253-
254250
if let statusMessage = helperManager.statusMessage, statusMessage.isEmpty == false {
255251
return statusMessage
256252
}
257253

254+
if fanController.mode.requiresPrivilegedHelper == false {
255+
return "Current cooling mode does not require the helper."
256+
}
257+
258258
switch helperManager.connectionState {
259259
case .reachable:
260260
return "The privileged helper is installed and responding."

0 commit comments

Comments
 (0)