@capgo/capacitor-updater 8.51.2 → 8.51.3
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.
|
@@ -146,7 +146,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
146
146
|
static final int APPLICATION_EXIT_REASON_USER_REQUESTED = 10;
|
|
147
147
|
static final int APPLICATION_EXIT_REASON_DEPENDENCY_DIED = 12;
|
|
148
148
|
|
|
149
|
-
private final String pluginVersion = "8.51.
|
|
149
|
+
private final String pluginVersion = "8.51.3";
|
|
150
150
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
151
151
|
|
|
152
152
|
private SharedPreferences.Editor editor;
|
|
@@ -92,7 +92,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
92
92
|
CAPPluginMethod(name: "completeFlexibleUpdate", returnType: CAPPluginReturnPromise)
|
|
93
93
|
]
|
|
94
94
|
public var implementation = CapgoUpdater()
|
|
95
|
-
private let pluginVersion: String = "8.51.
|
|
95
|
+
private let pluginVersion: String = "8.51.3"
|
|
96
96
|
private let launchStartedAtMs = Int64(Date().timeIntervalSince1970 * 1000)
|
|
97
97
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
98
98
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
@@ -707,13 +707,42 @@ import UIKit
|
|
|
707
707
|
}
|
|
708
708
|
}
|
|
709
709
|
|
|
710
|
-
private func populateDeltaCacheAsync(for id: String) {
|
|
710
|
+
private func populateDeltaCacheAsync(for id: String, manifest: [ManifestEntry]? = nil, sessionKey: String = "") {
|
|
711
711
|
DispatchQueue.global(qos: .utility).async { [weak self] in
|
|
712
|
-
self?.populateDeltaCache(for: id)
|
|
712
|
+
self?.populateDeltaCache(for: id, manifest: manifest, sessionKey: sessionKey)
|
|
713
713
|
}
|
|
714
714
|
}
|
|
715
715
|
|
|
716
|
-
|
|
716
|
+
struct ManifestLookupEntry {
|
|
717
|
+
let hash: String
|
|
718
|
+
/// The manifest's own file name, `.br` suffix included when present. The
|
|
719
|
+
/// built-in bundle stores files under this exact name (see
|
|
720
|
+
/// isManifestEntryAvailableLocally), unlike the extracted/cached copy which
|
|
721
|
+
/// is always named without the suffix.
|
|
722
|
+
let originalFileName: String
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/// Keys are stripped of the `.br` suffix so they match the extracted file names on
|
|
726
|
+
/// disk, not the manifest's (possibly brotli-compressed) original file names.
|
|
727
|
+
func manifestHashLookup(manifest: [ManifestEntry]?, sessionKey: String) -> [String: ManifestLookupEntry] {
|
|
728
|
+
guard let manifest else {
|
|
729
|
+
return [:]
|
|
730
|
+
}
|
|
731
|
+
var lookup: [String: ManifestLookupEntry] = [:]
|
|
732
|
+
for entry in manifest {
|
|
733
|
+
guard let fileName = entry.file_name,
|
|
734
|
+
let hash = resolveManifestFileHash(entry: entry, sessionKey: sessionKey) else {
|
|
735
|
+
continue
|
|
736
|
+
}
|
|
737
|
+
let destFileName = fileName.hasSuffix(".br") ? String(fileName.dropLast(3)) : fileName
|
|
738
|
+
lookup[destFileName] = ManifestLookupEntry(hash: hash, originalFileName: fileName)
|
|
739
|
+
}
|
|
740
|
+
return lookup
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/// `manifest` must only contain entries the caller already checksum-verified
|
|
744
|
+
/// (as `downloadManifest` does) — the hashes are trusted as-is, not re-checked.
|
|
745
|
+
func populateDeltaCache(for id: String, manifest: [ManifestEntry]? = nil, sessionKey: String = "") {
|
|
717
746
|
let bundleDir = self.getBundleDirectory(id: id)
|
|
718
747
|
let fileManager = FileManager.default
|
|
719
748
|
|
|
@@ -733,17 +762,33 @@ import UIKit
|
|
|
733
762
|
return
|
|
734
763
|
}
|
|
735
764
|
|
|
765
|
+
let knownEntries = manifestHashLookup(manifest: manifest, sessionKey: sessionKey)
|
|
766
|
+
let builtinFolder = self.builtinFolderURL()
|
|
767
|
+
|
|
736
768
|
for case let fileURL as URL in enumerator {
|
|
737
769
|
let resourceValues = try? fileURL.resourceValues(forKeys: [.isDirectoryKey])
|
|
738
770
|
if resourceValues?.isDirectory == true {
|
|
739
771
|
continue
|
|
740
772
|
}
|
|
741
773
|
|
|
742
|
-
let
|
|
774
|
+
let relativePath = String(fileURL.path.dropFirst(bundleDir.path.count + 1))
|
|
775
|
+
let knownEntry = knownEntries[relativePath]
|
|
776
|
+
|
|
777
|
+
let checksum = knownEntry?.hash ?? CryptoCipher.calcChecksum(filePath: fileURL)
|
|
743
778
|
if checksum.isEmpty {
|
|
744
779
|
continue
|
|
745
780
|
}
|
|
746
781
|
|
|
782
|
+
// Builtin is already a permanent reuse source (see isManifestEntryAvailableLocally),
|
|
783
|
+
// so there's no need to also duplicate this file into the delta cache
|
|
784
|
+
let builtinRelativePath = knownEntry?.originalFileName ?? relativePath
|
|
785
|
+
let builtinFilePath = builtinFolder.appendingPathComponent(builtinRelativePath)
|
|
786
|
+
let isBuiltinOrigin = fileManager.fileExists(atPath: builtinFilePath.path) &&
|
|
787
|
+
verifyChecksum(file: builtinFilePath, expectedHash: checksum)
|
|
788
|
+
if isBuiltinOrigin {
|
|
789
|
+
continue
|
|
790
|
+
}
|
|
791
|
+
|
|
747
792
|
let cacheFile = cacheFolder.appendingPathComponent("\(checksum)_\(fileURL.lastPathComponent)")
|
|
748
793
|
if fileManager.fileExists(atPath: cacheFile.path) {
|
|
749
794
|
continue
|
|
@@ -931,6 +976,12 @@ import UIKit
|
|
|
931
976
|
return actualHash == expectedHash
|
|
932
977
|
}
|
|
933
978
|
|
|
979
|
+
/// Overridable so tests can point it at a writable directory instead of the
|
|
980
|
+
/// real (read-only) app bundle.
|
|
981
|
+
func builtinFolderURL() -> URL {
|
|
982
|
+
Bundle.main.bundleURL.appendingPathComponent("public")
|
|
983
|
+
}
|
|
984
|
+
|
|
934
985
|
private func resolveManifestFileHash(entry: ManifestEntry, sessionKey: String) -> String? {
|
|
935
986
|
guard var fileHash = entry.file_hash, !fileHash.isEmpty else {
|
|
936
987
|
return nil
|
|
@@ -953,7 +1004,7 @@ import UIKit
|
|
|
953
1004
|
return false
|
|
954
1005
|
}
|
|
955
1006
|
|
|
956
|
-
let builtinFolder =
|
|
1007
|
+
let builtinFolder = self.builtinFolderURL()
|
|
957
1008
|
let builtinFilePath = builtinFolder.appendingPathComponent(fileName)
|
|
958
1009
|
if FileManager.default.fileExists(atPath: builtinFilePath.path) && verifyChecksum(file: builtinFilePath, expectedHash: fileHash) {
|
|
959
1010
|
return true
|
|
@@ -1066,7 +1117,7 @@ import UIKit
|
|
|
1066
1117
|
let id = self.randomString(length: 10)
|
|
1067
1118
|
logger.info("downloadManifest start \(id)")
|
|
1068
1119
|
let destFolder = self.getBundleDirectory(id: id)
|
|
1069
|
-
let builtinFolder =
|
|
1120
|
+
let builtinFolder = self.builtinFolderURL()
|
|
1070
1121
|
|
|
1071
1122
|
// Check disk space before starting manifest download (estimate 100KB per file, minimum 50MB)
|
|
1072
1123
|
let estimatedSize = Int64(max(manifest.count * 100 * 1024, 50 * 1024 * 1024))
|
|
@@ -1253,6 +1304,8 @@ import UIKit
|
|
|
1253
1304
|
// Send stats for manifest download complete
|
|
1254
1305
|
self.sendStats(action: "download_manifest_complete", versionName: version)
|
|
1255
1306
|
|
|
1307
|
+
self.populateDeltaCacheAsync(for: id, manifest: manifest, sessionKey: sessionKey)
|
|
1308
|
+
|
|
1256
1309
|
self.notifyDownload(id: id, percent: 100, bundle: updatedBundle)
|
|
1257
1310
|
logger.info("downloadManifest done \(id)")
|
|
1258
1311
|
return updatedBundle
|