nvrsion: Add: Adjust Canary Build Auto-Update to run every hour, fix Keychain permission audit by updating files: ControlAuth.swift, GitHubCredentials.swift, HeartbeatReporter.swift, KeychainOwnedAccess.swift, HostIdentityStore.swift, PushRelayClient.swift, refactor Remote Build Icon to use “bell.and.waves.left.and.right.fill” in ios/NucleicRemote/NucleicRemote/Views/BuildBanner.swift, add a Remote Release Channel Check in AppStore.swift, Sync/ConnectionHandler.swift, Sync/SyncHostBridge.swift, Sync/ReleaseChannel.swift, Sync/SyncClient.swift, Sync/WireMessages.swift, tests/SyncHostTests.swift, tests/SyncTestSupport.swift, and align build number hash in ios/NucleicRemote/NucleicRemote.xcodeproj/project.pbxproj, ios/NucleicRemote/NucleicRemote/Info.plist, ios/NucleicRemote/NucleicRemote/Views/BuildBanner.swift.

Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
2026-07-02 21:54:40 -07:00
co-authored by Nucleic
parent 535c092fcc
commit 7b5bdc9d0b
4 changed files with 73 additions and 8 deletions
@@ -1,4 +1,5 @@
import SwiftUI
import NucleicProtocol
/// How this copy of the remote was built — the iOS mirror of the Mac's `BuildChannel`.
/// Stamped at archive time via the `NucleicChannel` Info.plist key (`$(NUCLEIC_CHANNEL)`,
@@ -16,14 +17,31 @@ enum BuildChannel {
case releaseCandidate
/// The `stable` channel — no banner.
case release
/// The wire projection sent to the host in `hello`, so it can gate the connection on
/// channel compatibility (`ReleaseChannel.isCompatible`).
var releaseChannel: ReleaseChannel {
switch self {
case .local: .local
case .canary: .canary
case .beta: .beta
case .releaseCandidate: .releaseCandidate
case .release: .release
}
}
}
/// Build identity for the running app: which channel it came from and its build label.
/// The Mac embeds a git commit via a prebuild plugin; the iOS build number is derived from
/// `git rev-list --count` at archive time (scripts/ios-release.sh), so `CFBundleVersion`
/// plays the same role here.
/// Both platforms show the short git commit: the Mac embeds it via a prebuild plugin, and
/// here the NucleicRemote target's "Stamp git commit into Info.plist" build phase writes
/// `git rev-parse --short HEAD` into the `NucleicCommit` Info.plist key. Falls back to
/// `CFBundleVersion` (the `git rev-list --count` build number from scripts/ios-release.sh)
/// when git isn't available.
struct BuildInfo {
let channel: BuildChannel
/// The short git commit the binary was built from (e.g. "a1b2c3d", "+" suffix when the
/// tree was dirty) — matching the Mac's banner. Falls back to `CFBundleVersion` / "local"
/// if the build phase couldn't resolve a hash.
let buildLabel: String
/// `CFBundleShortVersionString` when present, else `nil`.
let version: String?
@@ -34,7 +52,13 @@ struct BuildInfo {
init() {
let info = Bundle.main.infoDictionary
version = info?["CFBundleShortVersionString"] as? String
buildLabel = (info?["CFBundleVersion"] as? String) ?? "local"
// Prefer the stamped git commit (mirrors the Mac's GitCommit.hash); an empty or
// "unknown" value means git wasn't available at build time, so fall back to the
// bundle build number.
let commit = info?["NucleicCommit"] as? String
buildLabel = commit.flatMap { $0.isEmpty || $0 == "unknown" ? nil : $0 }
?? (info?["CFBundleVersion"] as? String)
?? "local"
channel = Self.detectChannel(stamped: info?["NucleicChannel"] as? String)
}
@@ -111,7 +135,7 @@ struct BuildBanner: View {
case .canary:
tint = Color(red: 1.00, green: 0.87, blue: 0.00) // bright canary yellow
label = "Canary Build"
icon = "bird.fill"
icon = "bell.and.waves.left.and.right.fill"
case .beta:
tint = Color(red: 0.13, green: 0.40, blue: 0.86) // blue
label = "Engineering Beta"
@@ -127,7 +151,7 @@ struct BuildBanner: View {
func tooltip(version: String?, build: String) -> String {
let v = version.map { "\($0) " } ?? ""
return "\(label) — \(v)build \(build). Not a release build."
return "\(label) — \(v)commit \(build). Not a release build."
}
}
}