@hot-updater/react-native 0.36.6 → 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +357 -6
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +268 -34
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +75 -9
- package/android/src/main/java/com/hotupdater/NativeConfigUtils.kt +2 -16
- package/android/src/main/java/com/hotupdater/ReleaseCatalogCacheService.kt +166 -0
- package/android/src/main/java/com/hotupdater/SignatureVerifier.kt +1 -2
- package/android/src/newarch/HotUpdater.kt +1 -1
- package/android/src/newarch/HotUpdaterModule.kt +116 -0
- package/android/src/oldarch/HotUpdater.kt +1 -1
- package/android/src/oldarch/HotUpdaterModule.kt +128 -0
- package/android/src/oldarch/HotUpdaterSpec.kt +38 -0
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +280 -2
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +57 -2
- package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +118 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +491 -30
- package/ios/HotUpdater/Internal/BundleMetadata.swift +229 -6
- package/ios/HotUpdater/Internal/HotUpdater.mm +122 -0
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +176 -7
- package/ios/HotUpdater/Internal/ReleaseCatalogCacheService.swift +213 -0
- package/lib/commonjs/catalogCacheNative.js +44 -0
- package/lib/commonjs/catalogCacheNative.js.map +1 -0
- package/lib/commonjs/catalogCacheNative.spec.js +31 -0
- package/lib/commonjs/catalogCacheNative.spec.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +231 -60
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +604 -0
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -0
- package/lib/commonjs/error.js +8 -1
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/{fetchUpdateInfo.js → fetchJSON.js} +11 -14
- package/lib/commonjs/fetchJSON.js.map +1 -0
- package/lib/commonjs/httpClient.js +153 -0
- package/lib/commonjs/httpClient.js.map +1 -0
- package/lib/commonjs/httpClient.spec.js +102 -0
- package/lib/commonjs/httpClient.spec.js.map +1 -0
- package/lib/commonjs/index.js +48 -71
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.spec.js +117 -161
- package/lib/commonjs/index.spec.js.map +1 -1
- package/lib/commonjs/iosInterop.spec.js +18 -0
- package/lib/commonjs/iosInterop.spec.js.map +1 -0
- package/lib/commonjs/native.js +222 -55
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +226 -12
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js +218 -0
- package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.js +133 -0
- package/lib/commonjs/notifyAppReadyAnalytics.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.spec.js +246 -0
- package/lib/commonjs/notifyAppReadyAnalytics.spec.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js +24 -0
- package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js +117 -0
- package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +179 -0
- package/lib/commonjs/releaseCatalogCache.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.spec.js +195 -0
- package/lib/commonjs/releaseCatalogCache.spec.js.map +1 -0
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +64 -0
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +1 -0
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/commonjs/sdkVersion.js.map +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/types.js +0 -12
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/wrap.js +7 -82
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/commonjs/wrap.spec.js +203 -56
- package/lib/commonjs/wrap.spec.js.map +1 -1
- package/lib/commonjs/wrap.types.js +6 -0
- package/lib/commonjs/wrap.types.js.map +1 -0
- package/lib/module/catalogCacheNative.js +36 -0
- package/lib/module/catalogCacheNative.js.map +1 -0
- package/lib/module/catalogCacheNative.spec.js +31 -0
- package/lib/module/catalogCacheNative.spec.js.map +1 -0
- package/lib/module/checkForUpdate.js +233 -62
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +603 -0
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -0
- package/lib/module/error.js +6 -0
- package/lib/module/error.js.map +1 -1
- package/lib/module/{fetchUpdateInfo.js → fetchJSON.js} +9 -12
- package/lib/module/fetchJSON.js.map +1 -0
- package/lib/module/httpClient.js +148 -0
- package/lib/module/httpClient.js.map +1 -0
- package/lib/module/httpClient.spec.js +102 -0
- package/lib/module/httpClient.spec.js.map +1 -0
- package/lib/module/index.js +49 -66
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.spec.js +117 -161
- package/lib/module/index.spec.js.map +1 -1
- package/lib/module/iosInterop.spec.js +18 -0
- package/lib/module/iosInterop.spec.js.map +1 -0
- package/lib/module/native.js +206 -51
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +226 -12
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/notifyAppReadyAnalytics.boundary.spec.js +217 -0
- package/lib/module/notifyAppReadyAnalytics.boundary.spec.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.js +128 -0
- package/lib/module/notifyAppReadyAnalytics.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.spec.js +245 -0
- package/lib/module/notifyAppReadyAnalytics.spec.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.test-utils.js +18 -0
- package/lib/module/notifyAppReadyAnalytics.test-utils.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.transport.spec.js +116 -0
- package/lib/module/notifyAppReadyAnalytics.transport.spec.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +171 -0
- package/lib/module/releaseCatalogCache.js.map +1 -0
- package/lib/module/releaseCatalogCache.spec.js +195 -0
- package/lib/module/releaseCatalogCache.spec.js.map +1 -0
- package/lib/module/releaseCatalogCacheNativeContract.spec.js +64 -0
- package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +1 -0
- package/lib/module/sdkVersion.js +1 -1
- package/lib/module/sdkVersion.js.map +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/types.js +0 -12
- package/lib/module/types.js.map +1 -1
- package/lib/module/wrap.js +6 -81
- package/lib/module/wrap.js.map +1 -1
- package/lib/module/wrap.spec.js +203 -56
- package/lib/module/wrap.spec.js.map +1 -1
- package/lib/module/wrap.types.js +4 -0
- package/lib/module/wrap.types.js.map +1 -0
- package/lib/typescript/commonjs/catalogCacheNative.d.ts +4 -0
- package/lib/typescript/commonjs/catalogCacheNative.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +14 -4
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/error.d.ts +3 -0
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchJSON.d.ts +6 -0
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -0
- package/lib/typescript/commonjs/httpClient.d.ts +56 -0
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +32 -41
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +83 -18
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts +13 -0
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts.map +1 -0
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts +8 -0
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +25 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -0
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +40 -5
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +0 -118
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.d.ts +2 -191
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +65 -0
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -0
- package/lib/typescript/module/catalogCacheNative.d.ts +4 -0
- package/lib/typescript/module/catalogCacheNative.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +14 -4
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/error.d.ts +3 -0
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchJSON.d.ts +6 -0
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -0
- package/lib/typescript/module/httpClient.d.ts +56 -0
- package/lib/typescript/module/httpClient.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +32 -41
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +83 -18
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts +13 -0
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts.map +1 -0
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts +8 -0
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +25 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -0
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +40 -5
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +0 -118
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/wrap.d.ts +2 -191
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +65 -0
- package/lib/typescript/module/wrap.types.d.ts.map +1 -0
- package/package.json +6 -24
- package/src/catalogCacheNative.spec.ts +63 -0
- package/src/catalogCacheNative.ts +74 -0
- package/src/checkForUpdate.releaseCatalog.spec.ts +596 -0
- package/src/checkForUpdate.ts +368 -97
- package/src/error.ts +7 -0
- package/src/{fetchUpdateInfo.ts → fetchJSON.ts} +8 -15
- package/src/httpClient.spec.ts +132 -0
- package/src/httpClient.ts +261 -0
- package/src/index.spec.ts +110 -203
- package/src/index.ts +70 -93
- package/src/iosInterop.spec.ts +30 -0
- package/src/native.spec.ts +226 -10
- package/src/native.ts +385 -83
- package/src/notifyAppReadyAnalytics.boundary.spec.ts +234 -0
- package/src/notifyAppReadyAnalytics.spec.ts +243 -0
- package/src/notifyAppReadyAnalytics.test-utils.ts +31 -0
- package/src/notifyAppReadyAnalytics.transport.spec.ts +130 -0
- package/src/notifyAppReadyAnalytics.ts +194 -0
- package/src/releaseCatalogCache.spec.ts +282 -0
- package/src/releaseCatalogCache.ts +341 -0
- package/src/releaseCatalogCacheNativeContract.spec.ts +80 -0
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +51 -5
- package/src/types.ts +0 -140
- package/src/wrap.spec.ts +251 -56
- package/src/wrap.tsx +23 -318
- package/src/wrap.types.ts +78 -0
- package/android/src/main/java/com/hotupdater/StringResourceUtils.kt +0 -45
- package/app.plugin.js +0 -1
- package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.h +0 -7
- package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.mm +0 -4
- package/lib/commonjs/DefaultResolver.js +0 -50
- package/lib/commonjs/DefaultResolver.js.map +0 -1
- package/lib/commonjs/DefaultResolver.spec.js +0 -144
- package/lib/commonjs/DefaultResolver.spec.js.map +0 -1
- package/lib/commonjs/fetchUpdateInfo.js.map +0 -1
- package/lib/module/DefaultResolver.js +0 -46
- package/lib/module/DefaultResolver.js.map +0 -1
- package/lib/module/DefaultResolver.spec.js +0 -144
- package/lib/module/DefaultResolver.spec.js.map +0 -1
- package/lib/module/fetchUpdateInfo.js.map +0 -1
- package/lib/typescript/commonjs/DefaultResolver.d.ts +0 -11
- package/lib/typescript/commonjs/DefaultResolver.d.ts.map +0 -1
- package/lib/typescript/commonjs/fetchUpdateInfo.d.ts +0 -7
- package/lib/typescript/commonjs/fetchUpdateInfo.d.ts.map +0 -1
- package/lib/typescript/module/DefaultResolver.d.ts +0 -11
- package/lib/typescript/module/DefaultResolver.d.ts.map +0 -1
- package/lib/typescript/module/fetchUpdateInfo.d.ts +0 -7
- package/lib/typescript/module/fetchUpdateInfo.d.ts.map +0 -1
- package/plugin/build/transformers.js +0 -314
- package/plugin/build/withHotUpdater.js +0 -228
- package/src/DefaultResolver.spec.ts +0 -193
- package/src/DefaultResolver.ts +0 -63
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import Foundation
|
|
2
|
+
import OSLog
|
|
3
|
+
|
|
4
|
+
private let bundleStorageLog = OSLog(
|
|
5
|
+
subsystem: "com.hotupdater",
|
|
6
|
+
category: "BundleStorage"
|
|
7
|
+
)
|
|
2
8
|
|
|
3
9
|
@_silgen_name("HotUpdaterApplyBsdiffPatch")
|
|
4
10
|
private func hotUpdaterApplyBsdiffPatchSymbol(
|
|
@@ -275,12 +281,21 @@ public protocol BundleStorageService {
|
|
|
275
281
|
|
|
276
282
|
// Bundle update
|
|
277
283
|
func updateBundle(bundleId: String, fileUrl: URL?, fileHash: String?, manifestUrl: URL?, manifestFileHash: String?, changedAssets: [String: ChangedAssetDescriptor]?, progressHandler: @escaping (UpdateProgressPayload) -> Void, completion: @escaping (Result<Bool, Error>) -> Void)
|
|
284
|
+
func stageReleaseSelection(_ selection: PersistedSelection) -> Bool
|
|
285
|
+
func acceptReleaseCatalog(catalogId: String, scopeKey: String, generation: Int64, catalogHash: String, channel: String, selectionContextHash: String) -> Bool
|
|
286
|
+
func getActiveUpdateState() -> [String: Any]
|
|
287
|
+
func isReleaseSelectionCurrent(catalogId: String, scopeKey: String, generation: Int64, catalogHash: String, channel: String, selectionContextHash: String) -> Bool
|
|
288
|
+
func commitReleaseSelection(_ selection: PersistedSelection) -> Bool
|
|
278
289
|
|
|
279
290
|
// Rollback support
|
|
280
291
|
func markLaunchCompleted(bundleId: String?)
|
|
281
292
|
func notifyAppReady() -> [String: Any]
|
|
282
293
|
func getCrashHistory() -> CrashedHistory
|
|
283
294
|
func clearCrashHistory() -> Bool
|
|
295
|
+
func getInstallId() -> String
|
|
296
|
+
func getUserId() -> String?
|
|
297
|
+
func getUsername() -> String?
|
|
298
|
+
func setUser(userId: String?, username: String?)
|
|
284
299
|
|
|
285
300
|
/**
|
|
286
301
|
* Gets the base URL for the current active bundle directory
|
|
@@ -308,6 +323,14 @@ public protocol BundleStorageService {
|
|
|
308
323
|
func resetChannel() -> Result<Bool, Error>
|
|
309
324
|
}
|
|
310
325
|
|
|
326
|
+
public extension BundleStorageService {
|
|
327
|
+
func stageReleaseSelection(_: PersistedSelection) -> Bool { false }
|
|
328
|
+
func acceptReleaseCatalog(catalogId _: String, scopeKey _: String, generation _: Int64, catalogHash _: String, channel _: String, selectionContextHash _: String) -> Bool { false }
|
|
329
|
+
func getActiveUpdateState() -> [String: Any] { [:] }
|
|
330
|
+
func isReleaseSelectionCurrent(catalogId _: String, scopeKey _: String, generation _: Int64, catalogHash _: String, channel _: String, selectionContextHash _: String) -> Bool { false }
|
|
331
|
+
func commitReleaseSelection(_: PersistedSelection) -> Bool { false }
|
|
332
|
+
}
|
|
333
|
+
|
|
311
334
|
class BundleFileStorageService: BundleStorageService {
|
|
312
335
|
private struct ActiveBundleMetadataSnapshot {
|
|
313
336
|
let activeBundleId: String
|
|
@@ -352,6 +375,29 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
352
375
|
private var currentLaunchReport: LaunchReport?
|
|
353
376
|
private let activeBundleMetadataLock = NSLock()
|
|
354
377
|
private var activeBundleMetadataSnapshot: ActiveBundleMetadataSnapshot?
|
|
378
|
+
private let builtInBundleIdProvider: () -> String
|
|
379
|
+
private let updateStrategyProvider: () -> UpdateStrategy
|
|
380
|
+
private let releaseStateLock = NSRecursiveLock()
|
|
381
|
+
private var pendingInstallSelections: [String: PersistedSelection] = [:]
|
|
382
|
+
|
|
383
|
+
private var currentInstallIdentity: InstallIdentity?
|
|
384
|
+
private var currentUserIdentity: UserIdentity?
|
|
385
|
+
|
|
386
|
+
private let protectedBundleStoreEntries: Set<String> = [
|
|
387
|
+
BundleMetadata.metadataFilename,
|
|
388
|
+
CrashedHistory.crashedHistoryFilename,
|
|
389
|
+
LaunchReport.launchReportFilename,
|
|
390
|
+
InstallIdentity.installIdentityFilename,
|
|
391
|
+
UserIdentity.userIdentityFilename,
|
|
392
|
+
]
|
|
393
|
+
|
|
394
|
+
private func normalizeIdentityValue(_ value: String?) -> String? {
|
|
395
|
+
guard let trimmed = value?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
396
|
+
!trimmed.isEmpty else {
|
|
397
|
+
return nil
|
|
398
|
+
}
|
|
399
|
+
return trimmed
|
|
400
|
+
}
|
|
355
401
|
|
|
356
402
|
private func emitArchiveProgress(
|
|
357
403
|
progressHandler: @escaping (UpdateProgressPayload) -> Void,
|
|
@@ -654,7 +700,13 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
654
700
|
return false
|
|
655
701
|
}
|
|
656
702
|
|
|
657
|
-
|
|
703
|
+
os_log(
|
|
704
|
+
"HotUpdaterBsdiffPatchApplied asset=%{public}@ baseBundleId=%{public}@",
|
|
705
|
+
log: bundleStorageLog,
|
|
706
|
+
type: .default,
|
|
707
|
+
assetPath,
|
|
708
|
+
patch.baseBundleId
|
|
709
|
+
)
|
|
658
710
|
return true
|
|
659
711
|
case .failure:
|
|
660
712
|
resetDiffProgressFile(
|
|
@@ -701,13 +753,21 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
701
753
|
downloadService: DownloadService,
|
|
702
754
|
decompressService: DecompressService,
|
|
703
755
|
preferences: PreferencesService,
|
|
704
|
-
isolationKey: String
|
|
756
|
+
isolationKey: String,
|
|
757
|
+
builtInBundleIdProvider: @escaping () -> String = { "" },
|
|
758
|
+
updateStrategyProvider: @escaping () -> UpdateStrategy = {
|
|
759
|
+
let fingerprintHash = HotUpdaterConfig.shared.fingerprintHash
|
|
760
|
+
?? Bundle.main.object(forInfoDictionaryKey: "HOT_UPDATER_FINGERPRINT_HASH") as? String
|
|
761
|
+
return (fingerprintHash?.isEmpty == false) ? .fingerprint : .appVersion
|
|
762
|
+
}) {
|
|
705
763
|
|
|
706
764
|
self.fileSystem = fileSystem
|
|
707
765
|
self.downloadService = downloadService
|
|
708
766
|
self.decompressService = decompressService
|
|
709
767
|
self.preferences = preferences
|
|
710
768
|
self.isolationKey = isolationKey
|
|
769
|
+
self.builtInBundleIdProvider = builtInBundleIdProvider
|
|
770
|
+
self.updateStrategyProvider = updateStrategyProvider
|
|
711
771
|
|
|
712
772
|
// Create queue for file operations
|
|
713
773
|
self.fileOperationQueue = DispatchQueue(label: "com.hotupdater.fileoperations",
|
|
@@ -744,6 +804,20 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
744
804
|
return URL(fileURLWithPath: storeDir).appendingPathComponent(LaunchReport.launchReportFilename)
|
|
745
805
|
}
|
|
746
806
|
|
|
807
|
+
private func installIdentityFileURL() -> URL? {
|
|
808
|
+
guard case .success(let storeDir) = bundleStoreDir() else {
|
|
809
|
+
return nil
|
|
810
|
+
}
|
|
811
|
+
return URL(fileURLWithPath: storeDir).appendingPathComponent(InstallIdentity.installIdentityFilename)
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
private func userIdentityFileURL() -> URL? {
|
|
815
|
+
guard case .success(let storeDir) = bundleStoreDir() else {
|
|
816
|
+
return nil
|
|
817
|
+
}
|
|
818
|
+
return URL(fileURLWithPath: storeDir).appendingPathComponent(UserIdentity.userIdentityFilename)
|
|
819
|
+
}
|
|
820
|
+
|
|
747
821
|
// MARK: - Metadata Operations
|
|
748
822
|
|
|
749
823
|
private func loadMetadataOrNull() -> BundleMetadata? {
|
|
@@ -790,13 +864,87 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
790
864
|
_ = report.save(to: file)
|
|
791
865
|
}
|
|
792
866
|
|
|
867
|
+
private func loadInstallIdentity() -> InstallIdentity? {
|
|
868
|
+
if let currentInstallIdentity {
|
|
869
|
+
return currentInstallIdentity
|
|
870
|
+
}
|
|
871
|
+
guard let file = installIdentityFileURL(),
|
|
872
|
+
let identity = InstallIdentity.load(from: file) else {
|
|
873
|
+
return nil
|
|
874
|
+
}
|
|
875
|
+
currentInstallIdentity = identity
|
|
876
|
+
return identity
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
private func saveInstallIdentity(_ identity: InstallIdentity) -> Bool {
|
|
880
|
+
currentInstallIdentity = identity
|
|
881
|
+
guard let file = installIdentityFileURL() else {
|
|
882
|
+
return false
|
|
883
|
+
}
|
|
884
|
+
return identity.save(to: file)
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
private func loadUserIdentity() -> UserIdentity? {
|
|
888
|
+
if let currentUserIdentity {
|
|
889
|
+
return currentUserIdentity
|
|
890
|
+
}
|
|
891
|
+
guard let file = userIdentityFileURL(),
|
|
892
|
+
let identity = UserIdentity.load(from: file) else {
|
|
893
|
+
return nil
|
|
894
|
+
}
|
|
895
|
+
currentUserIdentity = identity
|
|
896
|
+
return identity
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
private func saveUserIdentity(_ identity: UserIdentity?) -> Bool {
|
|
900
|
+
currentUserIdentity = identity
|
|
901
|
+
guard let file = userIdentityFileURL() else {
|
|
902
|
+
return false
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
guard let identity else {
|
|
906
|
+
if FileManager.default.fileExists(atPath: file.path) {
|
|
907
|
+
try? FileManager.default.removeItem(at: file)
|
|
908
|
+
}
|
|
909
|
+
return true
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
return identity.save(to: file)
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
private func resolveBuiltInBundleId() -> String? {
|
|
916
|
+
let bundleId = builtInBundleIdProvider().trimmingCharacters(in: .whitespacesAndNewlines)
|
|
917
|
+
return bundleId.isEmpty ? nil : bundleId
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
private func createPendingTransition(
|
|
921
|
+
fromBundleId: String,
|
|
922
|
+
toBundleId: String
|
|
923
|
+
) -> PendingBundleTransition {
|
|
924
|
+
PendingBundleTransition(
|
|
925
|
+
fromBundleId: fromBundleId,
|
|
926
|
+
toBundleId: toBundleId,
|
|
927
|
+
updateStrategy: updateStrategyProvider()
|
|
928
|
+
)
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
private func launchReport(for status: LaunchReportStatus, transition: PendingBundleTransition?) -> LaunchReport {
|
|
932
|
+
LaunchReport(
|
|
933
|
+
status: status,
|
|
934
|
+
fromBundleId: transition?.fromBundleId,
|
|
935
|
+
toBundleId: transition?.toBundleId,
|
|
936
|
+
updateStrategy: transition?.updateStrategy
|
|
937
|
+
)
|
|
938
|
+
}
|
|
939
|
+
|
|
793
940
|
private func createInitialMetadata() -> BundleMetadata {
|
|
794
941
|
let currentBundleId = getCachedBundleId()
|
|
795
942
|
return BundleMetadata(
|
|
796
943
|
isolationKey: isolationKey,
|
|
797
944
|
stableBundleId: nil,
|
|
798
945
|
stagingBundleId: currentBundleId,
|
|
799
|
-
verificationPending: false
|
|
946
|
+
verificationPending: false,
|
|
947
|
+
pendingTransition: nil
|
|
800
948
|
)
|
|
801
949
|
}
|
|
802
950
|
|
|
@@ -804,7 +952,7 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
804
952
|
if let stagingBundleId = metadata.stagingBundleId, !metadata.verificationPending {
|
|
805
953
|
return stagingBundleId
|
|
806
954
|
}
|
|
807
|
-
return metadata.stableBundleId
|
|
955
|
+
return metadata.stableBundleId ?? resolveBuiltInBundleId()
|
|
808
956
|
}
|
|
809
957
|
|
|
810
958
|
private func getActiveBundleId() -> String? {
|
|
@@ -1076,8 +1224,7 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1076
1224
|
for item in contents {
|
|
1077
1225
|
let fullPath = (storeDir as NSString).appendingPathComponent(item)
|
|
1078
1226
|
|
|
1079
|
-
|
|
1080
|
-
if item == "metadata.json" || item == "crashed-history.json" {
|
|
1227
|
+
if protectedBundleStoreEntries.contains(item) {
|
|
1081
1228
|
continue
|
|
1082
1229
|
}
|
|
1083
1230
|
|
|
@@ -1116,13 +1263,47 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1116
1263
|
return metadata.verificationPending && metadata.stagingBundleId != nil
|
|
1117
1264
|
}
|
|
1118
1265
|
|
|
1119
|
-
private func prepareMetadataForNewStagingBundle(_ metadata: BundleMetadata, bundleId: String) -> BundleMetadata {
|
|
1266
|
+
private func prepareMetadataForNewStagingBundle(_ metadata: BundleMetadata, bundleId: String) -> BundleMetadata? {
|
|
1120
1267
|
let currentVerifiedBundleId = getCurrentVerifiedBundleId(metadata).flatMap { $0 == bundleId ? nil : $0 }
|
|
1268
|
+
releaseStateLock.lock()
|
|
1269
|
+
let incomingSelection = pendingInstallSelections.removeValue(forKey: bundleId)
|
|
1270
|
+
releaseStateLock.unlock()
|
|
1271
|
+
if let incomingSelection, !isSelectionCurrent(incomingSelection) {
|
|
1272
|
+
return nil
|
|
1273
|
+
}
|
|
1274
|
+
let currentVerifiedSelection: PersistedSelection? = {
|
|
1275
|
+
if metadata.stagingBundleId != nil, !metadata.verificationPending {
|
|
1276
|
+
return metadata.stagingSelection
|
|
1277
|
+
}
|
|
1278
|
+
return metadata.stableSelection
|
|
1279
|
+
}()?.bundleId == bundleId ? nil : ({
|
|
1280
|
+
if metadata.stagingBundleId != nil, !metadata.verificationPending {
|
|
1281
|
+
return metadata.stagingSelection
|
|
1282
|
+
}
|
|
1283
|
+
return metadata.stableSelection
|
|
1284
|
+
}())
|
|
1285
|
+
let stagingSelection = incomingSelection ?? PersistedSelection.legacyBundle(bundleId)
|
|
1286
|
+
let fromSelection = currentVerifiedSelection
|
|
1287
|
+
?? PersistedSelection.legacyBundle(resolveBuiltInBundleId() ?? bundleId)
|
|
1288
|
+
let pendingTransition = currentVerifiedBundleId.map {
|
|
1289
|
+
createPendingTransition(fromBundleId: $0, toBundleId: bundleId)
|
|
1290
|
+
}
|
|
1121
1291
|
return BundleMetadata(
|
|
1122
1292
|
isolationKey: isolationKey,
|
|
1123
1293
|
stableBundleId: currentVerifiedBundleId,
|
|
1124
1294
|
stagingBundleId: bundleId,
|
|
1295
|
+
stableSelection: currentVerifiedSelection,
|
|
1296
|
+
stagingSelection: stagingSelection,
|
|
1125
1297
|
verificationPending: true,
|
|
1298
|
+
pendingTransition: pendingTransition,
|
|
1299
|
+
pendingSelectionTransition: PendingSelectionTransition(
|
|
1300
|
+
fromReleaseId: fromSelection.releaseId,
|
|
1301
|
+
fromBundleId: fromSelection.bundleId,
|
|
1302
|
+
toReleaseId: stagingSelection.releaseId,
|
|
1303
|
+
toBundleId: stagingSelection.bundleId
|
|
1304
|
+
),
|
|
1305
|
+
highestSeenCatalogs: metadata.highestSeenCatalogs,
|
|
1306
|
+
currentSelectionContexts: metadata.currentSelectionContexts,
|
|
1126
1307
|
updatedAt: Date().timeIntervalSince1970 * 1000
|
|
1127
1308
|
)
|
|
1128
1309
|
}
|
|
@@ -1133,6 +1314,10 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1133
1314
|
return false
|
|
1134
1315
|
}
|
|
1135
1316
|
|
|
1317
|
+
let pendingTransition = metadata.pendingTransition
|
|
1318
|
+
let pendingSelectionTransition = metadata.pendingSelectionTransition
|
|
1319
|
+
let stableSelection = metadata.stableSelection
|
|
1320
|
+
|
|
1136
1321
|
var crashedHistory = loadCrashedHistory()
|
|
1137
1322
|
crashedHistory.addEntry(stagingId)
|
|
1138
1323
|
let _ = saveCrashedHistory(crashedHistory)
|
|
@@ -1154,7 +1339,13 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1154
1339
|
isolationKey: isolationKey,
|
|
1155
1340
|
stableBundleId: nil,
|
|
1156
1341
|
stagingBundleId: fallbackBundleId,
|
|
1342
|
+
stableSelection: nil,
|
|
1343
|
+
stagingSelection: stableSelection,
|
|
1157
1344
|
verificationPending: false,
|
|
1345
|
+
pendingTransition: nil,
|
|
1346
|
+
pendingSelectionTransition: nil,
|
|
1347
|
+
highestSeenCatalogs: metadata.highestSeenCatalogs,
|
|
1348
|
+
currentSelectionContexts: metadata.currentSelectionContexts,
|
|
1158
1349
|
updatedAt: Date().timeIntervalSince1970 * 1000
|
|
1159
1350
|
)
|
|
1160
1351
|
|
|
@@ -1180,7 +1371,18 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1180
1371
|
try? fileSystem.removeItem(atPath: stagingDir)
|
|
1181
1372
|
}
|
|
1182
1373
|
|
|
1183
|
-
saveLaunchReport(
|
|
1374
|
+
saveLaunchReport(
|
|
1375
|
+
LaunchReport(
|
|
1376
|
+
status: .recovered,
|
|
1377
|
+
fromReleaseId: pendingSelectionTransition?.toReleaseId,
|
|
1378
|
+
fromBundleId: stagingId,
|
|
1379
|
+
toReleaseId: stableSelection?.releaseId,
|
|
1380
|
+
toBundleId: fallbackBundleId
|
|
1381
|
+
?? pendingTransition?.fromBundleId
|
|
1382
|
+
?? resolveBuiltInBundleId(),
|
|
1383
|
+
updateStrategy: pendingTransition?.updateStrategy ?? updateStrategyProvider()
|
|
1384
|
+
)
|
|
1385
|
+
)
|
|
1184
1386
|
return true
|
|
1185
1387
|
}
|
|
1186
1388
|
|
|
@@ -1195,6 +1397,170 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1195
1397
|
|
|
1196
1398
|
_ = rollbackPendingBundle(stagingBundleId)
|
|
1197
1399
|
}
|
|
1400
|
+
|
|
1401
|
+
private func releaseScopeStateKey(catalogId: String, scopeKey: String) -> String {
|
|
1402
|
+
"\(catalogId)|\(scopeKey)"
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
private func selectionContextValue(channel: String, selectionContextHash: String) -> String {
|
|
1406
|
+
"\(channel)\n\(selectionContextHash)"
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
func acceptReleaseCatalog(
|
|
1410
|
+
catalogId: String,
|
|
1411
|
+
scopeKey: String,
|
|
1412
|
+
generation: Int64,
|
|
1413
|
+
catalogHash: String,
|
|
1414
|
+
channel: String,
|
|
1415
|
+
selectionContextHash: String
|
|
1416
|
+
) -> Bool {
|
|
1417
|
+
releaseStateLock.lock()
|
|
1418
|
+
defer { releaseStateLock.unlock() }
|
|
1419
|
+
var metadata = loadMetadataOrNull() ?? createInitialMetadata()
|
|
1420
|
+
let stateKey = releaseScopeStateKey(catalogId: catalogId, scopeKey: scopeKey)
|
|
1421
|
+
if let highestSeen = metadata.highestSeenCatalogs[stateKey] {
|
|
1422
|
+
if generation < highestSeen.generation { return false }
|
|
1423
|
+
if generation == highestSeen.generation, catalogHash != highestSeen.catalogHash {
|
|
1424
|
+
return false
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
metadata.highestSeenCatalogs[stateKey] = CatalogHighWater(
|
|
1428
|
+
generation: generation,
|
|
1429
|
+
catalogHash: catalogHash
|
|
1430
|
+
)
|
|
1431
|
+
metadata.currentSelectionContexts[stateKey] = selectionContextValue(
|
|
1432
|
+
channel: channel,
|
|
1433
|
+
selectionContextHash: selectionContextHash
|
|
1434
|
+
)
|
|
1435
|
+
metadata.updatedAt = Date().timeIntervalSince1970 * 1000
|
|
1436
|
+
return saveMetadata(metadata)
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
private func activeSelection(_ metadata: BundleMetadata) -> PersistedSelection? {
|
|
1440
|
+
metadata.stagingSelection
|
|
1441
|
+
?? metadata.stagingBundleId.map(PersistedSelection.legacyBundle)
|
|
1442
|
+
?? metadata.stableSelection
|
|
1443
|
+
?? metadata.stableBundleId.map(PersistedSelection.legacyBundle)
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
func getActiveUpdateState() -> [String: Any] {
|
|
1447
|
+
releaseStateLock.lock()
|
|
1448
|
+
defer { releaseStateLock.unlock() }
|
|
1449
|
+
guard let metadata = loadMetadataOrNull() else {
|
|
1450
|
+
return [
|
|
1451
|
+
"activeSelection": NSNull(),
|
|
1452
|
+
"stableSelection": NSNull(),
|
|
1453
|
+
"verificationPending": false,
|
|
1454
|
+
"highestSeenCatalogs": [:]
|
|
1455
|
+
]
|
|
1456
|
+
}
|
|
1457
|
+
let highWaters = metadata.highestSeenCatalogs.mapValues { highWater in
|
|
1458
|
+
[
|
|
1459
|
+
"generation": NSNumber(value: highWater.generation),
|
|
1460
|
+
"catalogHash": highWater.catalogHash
|
|
1461
|
+
] as [String: Any]
|
|
1462
|
+
}
|
|
1463
|
+
return [
|
|
1464
|
+
"activeSelection": activeSelection(metadata)?.dictionary ?? NSNull(),
|
|
1465
|
+
"stableSelection": metadata.stableSelection?.dictionary ?? NSNull(),
|
|
1466
|
+
"verificationPending": metadata.verificationPending,
|
|
1467
|
+
"highestSeenCatalogs": highWaters
|
|
1468
|
+
]
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
func isReleaseSelectionCurrent(
|
|
1472
|
+
catalogId: String,
|
|
1473
|
+
scopeKey: String,
|
|
1474
|
+
generation: Int64,
|
|
1475
|
+
catalogHash: String,
|
|
1476
|
+
channel: String,
|
|
1477
|
+
selectionContextHash: String
|
|
1478
|
+
) -> Bool {
|
|
1479
|
+
releaseStateLock.lock()
|
|
1480
|
+
defer { releaseStateLock.unlock() }
|
|
1481
|
+
guard let metadata = loadMetadataOrNull() else { return false }
|
|
1482
|
+
let stateKey = releaseScopeStateKey(catalogId: catalogId, scopeKey: scopeKey)
|
|
1483
|
+
return metadata.highestSeenCatalogs[stateKey] == CatalogHighWater(
|
|
1484
|
+
generation: generation,
|
|
1485
|
+
catalogHash: catalogHash
|
|
1486
|
+
) && metadata.currentSelectionContexts[stateKey] == selectionContextValue(
|
|
1487
|
+
channel: channel,
|
|
1488
|
+
selectionContextHash: selectionContextHash
|
|
1489
|
+
)
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
private func isSelectionCurrent(_ selection: PersistedSelection) -> Bool {
|
|
1493
|
+
guard let catalogId = selection.catalogId,
|
|
1494
|
+
let scopeKey = selection.scopeKey,
|
|
1495
|
+
let generation = selection.generation,
|
|
1496
|
+
let catalogHash = selection.catalogHash,
|
|
1497
|
+
let selectionContextHash = selection.selectionContextHash else {
|
|
1498
|
+
return false
|
|
1499
|
+
}
|
|
1500
|
+
return isReleaseSelectionCurrent(
|
|
1501
|
+
catalogId: catalogId,
|
|
1502
|
+
scopeKey: scopeKey,
|
|
1503
|
+
generation: generation,
|
|
1504
|
+
catalogHash: catalogHash,
|
|
1505
|
+
channel: selection.channel,
|
|
1506
|
+
selectionContextHash: selectionContextHash
|
|
1507
|
+
)
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
func stageReleaseSelection(_ selection: PersistedSelection) -> Bool {
|
|
1511
|
+
releaseStateLock.lock()
|
|
1512
|
+
defer { releaseStateLock.unlock() }
|
|
1513
|
+
guard isSelectionCurrent(selection) else { return false }
|
|
1514
|
+
pendingInstallSelections[selection.bundleId] = selection
|
|
1515
|
+
return true
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
func commitReleaseSelection(_ selection: PersistedSelection) -> Bool {
|
|
1519
|
+
releaseStateLock.lock()
|
|
1520
|
+
defer { releaseStateLock.unlock() }
|
|
1521
|
+
guard isSelectionCurrent(selection) else { return false }
|
|
1522
|
+
var metadata = loadMetadataOrNull() ?? createInitialMetadata()
|
|
1523
|
+
if selection.kind == "BUNDLE" {
|
|
1524
|
+
if metadata.stagingSelection?.bundleId == selection.bundleId
|
|
1525
|
+
|| metadata.stagingBundleId == selection.bundleId {
|
|
1526
|
+
metadata.stagingBundleId = selection.bundleId
|
|
1527
|
+
metadata.stagingSelection = selection
|
|
1528
|
+
} else if metadata.stableSelection?.bundleId == selection.bundleId
|
|
1529
|
+
|| metadata.stableBundleId == selection.bundleId {
|
|
1530
|
+
metadata.stableBundleId = selection.bundleId
|
|
1531
|
+
metadata.stableSelection = selection
|
|
1532
|
+
} else {
|
|
1533
|
+
return false
|
|
1534
|
+
}
|
|
1535
|
+
} else {
|
|
1536
|
+
guard case .success = setBundleURL(localPath: nil) else { return false }
|
|
1537
|
+
metadata.stableBundleId = nil
|
|
1538
|
+
metadata.stableSelection = nil
|
|
1539
|
+
metadata.stagingBundleId = nil
|
|
1540
|
+
metadata.stagingSelection = selection
|
|
1541
|
+
metadata.verificationPending = false
|
|
1542
|
+
metadata.pendingTransition = nil
|
|
1543
|
+
metadata.pendingSelectionTransition = nil
|
|
1544
|
+
}
|
|
1545
|
+
metadata.updatedAt = Date().timeIntervalSince1970 * 1000
|
|
1546
|
+
guard saveMetadata(metadata) else { return false }
|
|
1547
|
+
do {
|
|
1548
|
+
try preferences.setItem(
|
|
1549
|
+
selection.channel,
|
|
1550
|
+
forKey: "HotUpdaterChannel"
|
|
1551
|
+
)
|
|
1552
|
+
} catch {
|
|
1553
|
+
return false
|
|
1554
|
+
}
|
|
1555
|
+
if selection.kind != "BUNDLE", case .success(let storeDir) = bundleStoreDir() {
|
|
1556
|
+
for item in (try? fileSystem.contentsOfDirectory(atPath: storeDir)) ?? [] {
|
|
1557
|
+
guard !protectedBundleStoreEntries.contains(item) else { continue }
|
|
1558
|
+
let path = (storeDir as NSString).appendingPathComponent(item)
|
|
1559
|
+
try? fileSystem.removeItem(atPath: path)
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
return true
|
|
1563
|
+
}
|
|
1198
1564
|
|
|
1199
1565
|
// MARK: - Directory Management
|
|
1200
1566
|
|
|
@@ -1386,8 +1752,7 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1386
1752
|
let bundles = contents.compactMap { item -> String? in
|
|
1387
1753
|
let fullPath = (storeDir as NSString).appendingPathComponent(item)
|
|
1388
1754
|
|
|
1389
|
-
|
|
1390
|
-
if item == "metadata.json" || item == "crashed-history.json" {
|
|
1755
|
+
if protectedBundleStoreEntries.contains(item) {
|
|
1391
1756
|
return nil
|
|
1392
1757
|
}
|
|
1393
1758
|
|
|
@@ -1608,6 +1973,10 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1608
1973
|
|
|
1609
1974
|
func prepareLaunch(bundle: Bundle, pendingRecovery: PendingCrashRecovery?) -> LaunchSelection {
|
|
1610
1975
|
saveLaunchReport(nil)
|
|
1976
|
+
if loadInstallIdentity() == nil {
|
|
1977
|
+
let identity = InstallIdentity()
|
|
1978
|
+
_ = saveInstallIdentity(identity)
|
|
1979
|
+
}
|
|
1611
1980
|
applyPendingRecoveryIfNeeded(pendingRecovery)
|
|
1612
1981
|
return selectLaunch(bundle: bundle)
|
|
1613
1982
|
}
|
|
@@ -1642,7 +2011,11 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1642
2011
|
let setResult = self.setBundleURL(localPath: nil)
|
|
1643
2012
|
switch setResult {
|
|
1644
2013
|
case .success:
|
|
1645
|
-
let
|
|
2014
|
+
let previousMetadata = self.loadMetadataOrNull()
|
|
2015
|
+
var resetMetadata = self.createInitialMetadata()
|
|
2016
|
+
resetMetadata.highestSeenCatalogs = previousMetadata?.highestSeenCatalogs ?? [:]
|
|
2017
|
+
resetMetadata.currentSelectionContexts = previousMetadata?.currentSelectionContexts ?? [:]
|
|
2018
|
+
let _ = self.saveMetadata(resetMetadata)
|
|
1646
2019
|
self.saveLaunchReport(nil)
|
|
1647
2020
|
let cleanupResult = self.cleanupOldBundles(currentBundleId: currentBundleId, bundleId: bundleId)
|
|
1648
2021
|
switch cleanupResult {
|
|
@@ -1680,11 +2053,18 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1680
2053
|
case .success(let existingBundlePath):
|
|
1681
2054
|
if let bundlePath = existingBundlePath {
|
|
1682
2055
|
NSLog("[BundleStorage] Using cached bundle at path: \(bundlePath)")
|
|
2056
|
+
let currentMetadata = self.loadMetadataOrNull() ?? self.createInitialMetadata()
|
|
2057
|
+
guard let updatedMetadata = self.prepareMetadataForNewStagingBundle(currentMetadata, bundleId: bundleId) else {
|
|
2058
|
+
completion(.failure(BundleStorageError.unknown(
|
|
2059
|
+
NSError(domain: "HotUpdater", code: 0, userInfo: [
|
|
2060
|
+
NSLocalizedDescriptionKey: "Release catalog selection is stale"
|
|
2061
|
+
])
|
|
2062
|
+
)))
|
|
2063
|
+
return
|
|
2064
|
+
}
|
|
1683
2065
|
let setResult = self.setBundleURL(localPath: bundlePath)
|
|
1684
2066
|
switch setResult {
|
|
1685
2067
|
case .success:
|
|
1686
|
-
let currentMetadata = self.loadMetadataOrNull() ?? self.createInitialMetadata()
|
|
1687
|
-
let updatedMetadata = self.prepareMetadataForNewStagingBundle(currentMetadata, bundleId: bundleId)
|
|
1688
2068
|
let _ = self.saveMetadata(updatedMetadata)
|
|
1689
2069
|
NSLog("[BundleStorage] Set staging bundle (cached): \(bundleId), verificationPending: true")
|
|
1690
2070
|
|
|
@@ -2113,10 +2493,18 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
2113
2493
|
from: tmpDir,
|
|
2114
2494
|
to: realDir
|
|
2115
2495
|
)
|
|
2496
|
+
let currentMetadata = self.loadMetadataOrNull() ?? self.createInitialMetadata()
|
|
2497
|
+
guard let updatedMetadata = self.prepareMetadataForNewStagingBundle(currentMetadata, bundleId: bundleId) else {
|
|
2498
|
+
self.cleanupTemporaryFiles([tempDirectory])
|
|
2499
|
+
completion(.failure(BundleStorageError.unknown(
|
|
2500
|
+
NSError(domain: "HotUpdater", code: 0, userInfo: [
|
|
2501
|
+
NSLocalizedDescriptionKey: "Release catalog selection is stale"
|
|
2502
|
+
])
|
|
2503
|
+
)))
|
|
2504
|
+
return
|
|
2505
|
+
}
|
|
2116
2506
|
switch self.setBundleURL(localPath: finalBundlePath) {
|
|
2117
2507
|
case .success:
|
|
2118
|
-
let currentMetadata = self.loadMetadataOrNull() ?? self.createInitialMetadata()
|
|
2119
|
-
let updatedMetadata = self.prepareMetadataForNewStagingBundle(currentMetadata, bundleId: bundleId)
|
|
2120
2508
|
let _ = self.saveMetadata(updatedMetadata)
|
|
2121
2509
|
self.cleanupTemporaryFiles([tempDirectory])
|
|
2122
2510
|
self.scheduleCleanupOldBundles(
|
|
@@ -2447,14 +2835,22 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
2447
2835
|
)
|
|
2448
2836
|
|
|
2449
2837
|
// 12) Set the bundle URL in preferences (for backwards compatibility)
|
|
2838
|
+
let currentMetadata = self.loadMetadataOrNull() ?? self.createInitialMetadata()
|
|
2839
|
+
guard let updatedMetadata = self.prepareMetadataForNewStagingBundle(currentMetadata, bundleId: bundleId) else {
|
|
2840
|
+
self.cleanupTemporaryFiles([tempDirectory])
|
|
2841
|
+
completion(.failure(BundleStorageError.unknown(
|
|
2842
|
+
NSError(domain: "HotUpdater", code: 0, userInfo: [
|
|
2843
|
+
NSLocalizedDescriptionKey: "Release catalog selection is stale"
|
|
2844
|
+
])
|
|
2845
|
+
)))
|
|
2846
|
+
return
|
|
2847
|
+
}
|
|
2450
2848
|
let setResult = self.setBundleURL(localPath: finalBundlePath)
|
|
2451
2849
|
switch setResult {
|
|
2452
2850
|
case .success:
|
|
2453
2851
|
NSLog("[BundleStorage] Successfully set bundle URL: \(finalBundlePath)")
|
|
2454
2852
|
|
|
2455
2853
|
// 13) Set staging metadata for rollback support
|
|
2456
|
-
let currentMetadata = self.loadMetadataOrNull() ?? self.createInitialMetadata()
|
|
2457
|
-
let updatedMetadata = self.prepareMetadataForNewStagingBundle(currentMetadata, bundleId: bundleId)
|
|
2458
2854
|
let _ = self.saveMetadata(updatedMetadata)
|
|
2459
2855
|
NSLog("[BundleStorage] Set staging bundle: \(bundleId), verificationPending: true")
|
|
2460
2856
|
|
|
@@ -2534,19 +2930,52 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
2534
2930
|
return
|
|
2535
2931
|
}
|
|
2536
2932
|
|
|
2933
|
+
let pendingTransition = metadata.pendingTransition
|
|
2934
|
+
let pendingSelectionTransition = metadata.pendingSelectionTransition
|
|
2935
|
+
metadata.stableBundleId = nil
|
|
2936
|
+
metadata.stableSelection = nil
|
|
2537
2937
|
metadata.verificationPending = false
|
|
2938
|
+
metadata.pendingTransition = nil
|
|
2939
|
+
metadata.pendingSelectionTransition = nil
|
|
2538
2940
|
metadata.updatedAt = Date().timeIntervalSince1970 * 1000
|
|
2539
2941
|
let _ = saveMetadata(metadata)
|
|
2942
|
+
saveLaunchReport(
|
|
2943
|
+
LaunchReport(
|
|
2944
|
+
status: .updateApplied,
|
|
2945
|
+
fromReleaseId: pendingSelectionTransition?.fromReleaseId,
|
|
2946
|
+
fromBundleId: pendingSelectionTransition?.fromBundleId ?? pendingTransition?.fromBundleId,
|
|
2947
|
+
toReleaseId: pendingSelectionTransition?.toReleaseId,
|
|
2948
|
+
toBundleId: pendingSelectionTransition?.toBundleId ?? pendingTransition?.toBundleId,
|
|
2949
|
+
updateStrategy: pendingTransition?.updateStrategy
|
|
2950
|
+
)
|
|
2951
|
+
)
|
|
2540
2952
|
}
|
|
2541
2953
|
|
|
2542
2954
|
func notifyAppReady() -> [String: Any] {
|
|
2543
2955
|
guard let report = loadLaunchReport() else {
|
|
2544
|
-
|
|
2956
|
+
if let metadata = loadMetadataOrNull(),
|
|
2957
|
+
metadata.verificationPending,
|
|
2958
|
+
metadata.stagingBundleId != nil {
|
|
2959
|
+
return ["status": "PENDING"]
|
|
2960
|
+
}
|
|
2961
|
+
return ["status": LaunchReportStatus.unchanged.rawValue]
|
|
2545
2962
|
}
|
|
2546
2963
|
|
|
2547
|
-
var result: [String: Any] = ["status": report.status]
|
|
2548
|
-
if let
|
|
2549
|
-
result["
|
|
2964
|
+
var result: [String: Any] = ["status": report.status.rawValue]
|
|
2965
|
+
if let fromReleaseId = report.fromReleaseId {
|
|
2966
|
+
result["fromReleaseId"] = fromReleaseId
|
|
2967
|
+
}
|
|
2968
|
+
if let fromBundleId = report.fromBundleId {
|
|
2969
|
+
result["fromBundleId"] = fromBundleId
|
|
2970
|
+
}
|
|
2971
|
+
if let toBundleId = report.toBundleId {
|
|
2972
|
+
result["toBundleId"] = toBundleId
|
|
2973
|
+
}
|
|
2974
|
+
if let toReleaseId = report.toReleaseId {
|
|
2975
|
+
result["toReleaseId"] = toReleaseId
|
|
2976
|
+
}
|
|
2977
|
+
if let updateStrategy = report.updateStrategy {
|
|
2978
|
+
result["updateStrategy"] = updateStrategy.rawValue
|
|
2550
2979
|
}
|
|
2551
2980
|
return result
|
|
2552
2981
|
}
|
|
@@ -2569,6 +2998,37 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
2569
2998
|
return saveCrashedHistory(history)
|
|
2570
2999
|
}
|
|
2571
3000
|
|
|
3001
|
+
func getInstallId() -> String {
|
|
3002
|
+
if let installId = loadInstallIdentity()?.installId {
|
|
3003
|
+
return installId
|
|
3004
|
+
}
|
|
3005
|
+
|
|
3006
|
+
let identity = InstallIdentity()
|
|
3007
|
+
guard saveInstallIdentity(identity) else {
|
|
3008
|
+
return identity.installId
|
|
3009
|
+
}
|
|
3010
|
+
return identity.installId
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
func getUserId() -> String? {
|
|
3014
|
+
loadUserIdentity()?.userId
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
func getUsername() -> String? {
|
|
3018
|
+
loadUserIdentity()?.username
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
func setUser(userId: String?, username: String?) {
|
|
3022
|
+
let normalizedUserId = normalizeIdentityValue(userId)
|
|
3023
|
+
let normalizedUsername = normalizeIdentityValue(username)
|
|
3024
|
+
let identity = UserIdentity(userId: normalizedUserId, username: normalizedUsername)
|
|
3025
|
+
if identity.isEmpty {
|
|
3026
|
+
_ = saveUserIdentity(nil)
|
|
3027
|
+
return
|
|
3028
|
+
}
|
|
3029
|
+
_ = saveUserIdentity(identity)
|
|
3030
|
+
}
|
|
3031
|
+
|
|
2572
3032
|
/**
|
|
2573
3033
|
* Gets the base URL for the current active bundle directory
|
|
2574
3034
|
* Returns the file:// URL to the bundle directory without trailing slash
|
|
@@ -2618,12 +3078,15 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
2618
3078
|
return .failure(BundleStorageError.unknown(nil))
|
|
2619
3079
|
}
|
|
2620
3080
|
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
3081
|
+
var clearedMetadata = loadMetadataOrNull() ?? BundleMetadata(isolationKey: isolationKey)
|
|
3082
|
+
clearedMetadata.stableBundleId = nil
|
|
3083
|
+
clearedMetadata.stagingBundleId = nil
|
|
3084
|
+
clearedMetadata.stableSelection = nil
|
|
3085
|
+
clearedMetadata.stagingSelection = nil
|
|
3086
|
+
clearedMetadata.verificationPending = false
|
|
3087
|
+
clearedMetadata.pendingTransition = nil
|
|
3088
|
+
clearedMetadata.pendingSelectionTransition = nil
|
|
3089
|
+
clearedMetadata.updatedAt = Date().timeIntervalSince1970 * 1000
|
|
2627
3090
|
|
|
2628
3091
|
guard saveMetadata(clearedMetadata) else {
|
|
2629
3092
|
return .failure(BundleStorageError.unknown(nil))
|
|
@@ -2637,9 +3100,7 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
2637
3100
|
|
|
2638
3101
|
do {
|
|
2639
3102
|
for item in try fileSystem.contentsOfDirectory(atPath: storeDir) {
|
|
2640
|
-
if item
|
|
2641
|
-
item == CrashedHistory.crashedHistoryFilename ||
|
|
2642
|
-
item == LaunchReport.launchReportFilename {
|
|
3103
|
+
if protectedBundleStoreEntries.contains(item) {
|
|
2643
3104
|
continue
|
|
2644
3105
|
}
|
|
2645
3106
|
|