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:
@@ -136,6 +136,7 @@
|
||||
BP00000000000000000003 /* Frameworks */,
|
||||
BP00000000000000000004 /* Resources */,
|
||||
BP00000000000000000005 /* Embed Foundation Extensions */,
|
||||
BP00000000000000000009 /* Stamp git commit into Info.plist */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -235,6 +236,24 @@
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
BP00000000000000000009 /* Stamp git commit into Info.plist */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Stamp git commit into Info.plist";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# Stamp the short git commit into the built Info.plist (NucleicCommit key), the iOS\n# mirror of the Mac's EmbedGitCommit prebuild plugin. Runs before code signing so the\n# signature covers the stamped value; alwaysOutOfDate keeps it fresh on every build. A\n# trailing \"+\" marks a dirty tree; \"unknown\" (no git) makes the app fall back to the\n# bundle build number.\nset -e\nplist=\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n[ -f \"$plist\" ] || exit 0\nhash=$(git -C \"${SRCROOT}\" rev-parse --short HEAD 2>/dev/null || echo unknown)\nif [ \"$hash\" != unknown ] && [ -n \"$(git -C \"${SRCROOT}\" status --porcelain 2>/dev/null)\" ]; then\n hash=\"${hash}+\"\nfi\n/usr/libexec/PlistBuddy -c \"Set :NucleicCommit $hash\" \"$plist\" 2>/dev/null || /usr/libexec/PlistBuddy -c \"Add :NucleicCommit string $hash\" \"$plist\"\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
BP00000000000000000002 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
@@ -305,6 +324,9 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = L7UDTQ6F5W;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
// The "Stamp git commit into Info.plist" phase shells out to git and rewrites the
|
||||
// built Info.plist, both of which script sandboxing would block.
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = NucleicRemote/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Nucleic$(NUCLEIC_NAME_SUFFIX)";
|
||||
@@ -349,6 +371,9 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = L7UDTQ6F5W;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
// The "Stamp git commit into Info.plist" phase shells out to git and rewrites the
|
||||
// built Info.plist, both of which script sandboxing would block.
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = NucleicRemote/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Nucleic$(NUCLEIC_NAME_SUFFIX)";
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
</array>
|
||||
<key>NucleicChannel</key>
|
||||
<string>$(NUCLEIC_CHANNEL)</string>
|
||||
<!-- The short git commit this build came from, shown in the non-release banner to match
|
||||
the Mac. Left empty here and overwritten in the built product by the NucleicRemote
|
||||
target's "Stamp git commit into Info.plist" build phase (git rev-parse --short HEAD). -->
|
||||
<key>NucleicCommit</key>
|
||||
<string></string>
|
||||
<key>NSSupportsLiveActivities</key>
|
||||
<true/>
|
||||
<!-- Export compliance: the app ships a Noise-Protocol secure channel (X25519 /
|
||||
|
||||
@@ -293,7 +293,8 @@ final class RemoteStore: ObservableObject {
|
||||
let client = SyncClient(
|
||||
channel: channel, identity: identity, hostStaticKey: payload.hostStaticKey,
|
||||
mode: .pair(secret: payload.pairingSecret), deviceID: deviceID,
|
||||
deviceLabel: UIDevice.current.name, pushToken: PushRegistrar.shared.tokenHex)
|
||||
deviceLabel: UIDevice.current.name, pushToken: PushRegistrar.shared.tokenHex,
|
||||
releaseChannel: BuildInfo.current.channel.releaseChannel)
|
||||
self.client = client
|
||||
consume(client, pairingPayload: payload)
|
||||
}
|
||||
@@ -312,7 +313,8 @@ final class RemoteStore: ObservableObject {
|
||||
let client = SyncClient(
|
||||
channel: channel, identity: identity, hostStaticKey: host.hostStaticKey,
|
||||
mode: .reconnect, deviceID: host.deviceID, deviceLabel: UIDevice.current.name,
|
||||
pushToken: PushRegistrar.shared.tokenHex)
|
||||
pushToken: PushRegistrar.shared.tokenHex,
|
||||
releaseChannel: BuildInfo.current.channel.releaseChannel)
|
||||
self.client = client
|
||||
consume(client, pairingPayload: nil)
|
||||
}
|
||||
@@ -781,6 +783,15 @@ final class RemoteStore: ObservableObject {
|
||||
openDiff = diff
|
||||
diffLoading = false
|
||||
case .wireError(let error):
|
||||
// A channel mismatch means the host and this remote were built from incompatible
|
||||
// release channels. Surface it as a persistent failure with a clear message rather
|
||||
// than a transient bubble; the follow-on `.closed` still schedules a backoff retry,
|
||||
// so the connection recovers on its own once either side is updated to a matching
|
||||
// channel.
|
||||
if error.code == .channelMismatch {
|
||||
connectivity = .failed(error.message)
|
||||
break
|
||||
}
|
||||
// Not fatal — surface as a transient bubble (the Mac's last-error overlay).
|
||||
// Losing an approval race isn't an error worth interrupting for; the card
|
||||
// collapses on the matching `approvalResolved`.
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user