This commit is contained in:
Geoffrey Goh
2016-01-20 16:57:37 -08:00
parent a44c7923ca
commit d20e9ccc1e
4 changed files with 99 additions and 139 deletions

View File

@@ -23,7 +23,7 @@ static NSString *const DeploymentFailed = @"DeploymentFailed";
// These keys represent the names we use to store data in NSUserDefaults
static NSString *const FailedUpdatesKey = @"CODE_PUSH_FAILED_UPDATES";
static NSString *const PendingUpdateKey = @"CODE_PUSH_PENDING_UPDATE";
static NSString *const StatusReportsKey = @"CODE_PUSH_STATUS_REPORTS";
static NSString *const LastDeploymentReportKey = @"CODE_PUSH_LAST_DEPLOYMENT_REPORT";
// These keys are already "namespaced" by the PendingUpdateKey, so
// their values don't need to be obfuscated to prevent collision with app data
@@ -211,8 +211,8 @@ static NSString *const PackageIsPendingKey = @"isPending";
- (BOOL)isDeploymentStatusNotYetReported:(NSString *)appVersionOrPackageIdentifier
{
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSDictionary *sentStatusReports = [preferences objectForKey:StatusReportsKey];
return sentStatusReports == nil || [sentStatusReports objectForKey:appVersionOrPackageIdentifier] == nil;
NSString *sentStatusReportIdentifier = [preferences objectForKey:LastDeploymentReportKey];
return sentStatusReportIdentifier == nil || ![sentStatusReportIdentifier isEqualToString:appVersionOrPackageIdentifier];
}
/*
@@ -286,18 +286,9 @@ static NSString *const PackageIsPendingKey = @"isPending";
}
- (void)recordDeploymentStatusReported:(NSString *)appVersionOrPackageIdentifier
status:(NSString *)status
{
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *sentStatusReports = [preferences objectForKey:StatusReportsKey];
if (sentStatusReports == nil) {
sentStatusReports = [NSMutableDictionary dictionary];
} else {
sentStatusReports = [sentStatusReports mutableCopy];
}
[sentStatusReports setValue:status forKey:appVersionOrPackageIdentifier];
[preferences setValue:sentStatusReports forKey:StatusReportsKey];
[preferences setValue:LastDeploymentReportKey forKey:appVersionOrPackageIdentifier];
[preferences synchronize];
}
@@ -543,45 +534,48 @@ RCT_EXPORT_METHOD(getNewStatusReport:(RCTPromiseResolveBlock)resolve
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey];
if (failedUpdates) {
NSDictionary* lastFailedPackage = [failedUpdates lastObject];
NSDictionary *lastFailedPackage = [failedUpdates lastObject];
if (lastFailedPackage) {
NSString* lastFailedPackageIdentifier = [self getPackageStatusReportIdentifier:lastFailedPackage];
NSString *lastFailedPackageIdentifier = [self getPackageStatusReportIdentifier:lastFailedPackage];
if (lastFailedPackageIdentifier && [self isDeploymentStatusNotYetReported:lastFailedPackageIdentifier]) {
[self recordDeploymentStatusReported:lastFailedPackageIdentifier
status:DeploymentFailed];
[self recordDeploymentStatusReported:lastFailedPackageIdentifier];
resolve(@{ @"package": lastFailedPackage, @"status": DeploymentFailed });
return;
}
}
}
}
if (_isFirstRunAfterUpdate) {
} else if (_isFirstRunAfterUpdate) {
// Check if the current CodePush package has been reported
NSError *error;
NSDictionary* currentPackage = [CodePushPackage getCurrentPackage:&error];
if (currentPackage) {
NSString* currentPackageIdentifier = [self getPackageStatusReportIdentifier:currentPackage];
if (currentPackageIdentifier && [self isDeploymentStatusNotYetReported:currentPackageIdentifier]) {
[self recordDeploymentStatusReported:currentPackageIdentifier
status:DeploymentSucceeded];
[self recordDeploymentStatusReported:currentPackageIdentifier];
resolve(@{ @"package": currentPackage, @"status": DeploymentSucceeded });
return;
}
}
} else {
NSError *error;
NSString *currentPackageHash = [CodePushPackage getCurrentPackageHash:&error];
if (error || currentPackageHash == nil) {
// Check if the current appVersion has been reported. Use date as the binary identifier to
// handle binary releases that do not modify the appVersion.
NSURL *binaryJsBundleUrl = [CodePush bundleURL];
NSDictionary *binaryFileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[binaryJsBundleUrl path] error:nil];
NSTimeInterval binaryDate = [[binaryFileAttributes objectForKey:NSFileModificationDate] timeIntervalSince1970];
NSString* binaryIdentifier = [NSString stringWithFormat:@"%f", binaryDate];
if ([self isDeploymentStatusNotYetReported:binaryIdentifier]) {
[self recordDeploymentStatusReported:binaryIdentifier];
resolve(@{ @"appVersion": [[CodePushConfig current] appVersion] });
return;
}
}
}
// Check if the current appVersion has been reported.
NSString *appVersion = [[CodePushConfig current] appVersion];
if ([self isDeploymentStatusNotYetReported:appVersion]) {
[self recordDeploymentStatusReported:appVersion
status:DeploymentSucceeded];
resolve(@{ @"appVersion": appVersion });
return;
}
resolve([NSNull null]);
return;
}
/*

View File

@@ -61,7 +61,7 @@ public class CodePush {
private final String PENDING_UPDATE_IS_LOADING_KEY = "isLoading";
private final String PENDING_UPDATE_KEY = "CODE_PUSH_PENDING_UPDATE";
private final String RESOURCES_BUNDLE = "resources.arsc";
private final String STATUS_REPORTS_KEY = "CODE_PUSH_STATUS_REPORTS";
private final String LAST_DEPLOYMENT_REPORT_KEY = "CODE_PUSH_LAST_DEPLOYMENT_REPORT";
// This needs to be kept in sync with https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManager.java#L78
private final String REACT_DEV_BUNDLE_CACHE_FILE_NAME = "ReactNativeDevBundle.js";
@@ -153,10 +153,10 @@ public class CodePush {
binaryModifiedDateDuringPackageInstall = Long.parseLong(binaryModifiedDateDuringPackageInstallString);
}
String pacakgeAppVersion = CodePushUtils.tryGetString(packageMetadata, "appVersion");
String packageAppVersion = CodePushUtils.tryGetString(packageMetadata, "appVersion");
if (binaryModifiedDateDuringPackageInstall != null &&
binaryModifiedDateDuringPackageInstall == binaryResourcesModifiedTime &&
(this.isUsingTestConfiguration() || this.appVersion.equals(pacakgeAppVersion))) {
(this.isUsingTestConfiguration() || this.appVersion.equals(packageAppVersion))) {
CodePushUtils.logBundleUrl(packageFilePath);
return packageFilePath;
} else {
@@ -169,8 +169,6 @@ public class CodePush {
CodePushUtils.logBundleUrl(binaryJsBundleUrl);
return binaryJsBundleUrl;
}
} catch (IOException e) {
throw new CodePushUnknownException("Error in getting current package bundle path", e);
} catch (NumberFormatException e) {
throw new CodePushUnknownException("Error in reading binary modified date from package metadata", e);
}
@@ -263,17 +261,11 @@ public class CodePush {
private boolean isDeploymentStatusNotYetReported(String appVersionOrPackageIdentifier) {
SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0);
String sentStatusReportsString = settings.getString(STATUS_REPORTS_KEY, null);
if (sentStatusReportsString == null) {
String lastDeploymentReportIdentifier = settings.getString(LAST_DEPLOYMENT_REPORT_KEY, null);
if (lastDeploymentReportIdentifier == null) {
return true;
} else {
try {
JSONObject sentStatusReports = new JSONObject(sentStatusReportsString);
return !sentStatusReports.has(appVersionOrPackageIdentifier);
} catch (JSONException e) {
throw new CodePushUnknownException("Unable to parse sent status reports information " +
sentStatusReportsString + " stored in SharedPreferences.", e);
}
return !lastDeploymentReportIdentifier.equals(appVersionOrPackageIdentifier);
}
}
@@ -311,22 +303,9 @@ public class CodePush {
}
}
private void recordDeploymentStatusReported(String appVersionOrPackageIdentifier, String status) {
private void recordDeploymentStatusReported(String appVersionOrPackageIdentifier) {
SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0);
String sentStatusReportsString = settings.getString(STATUS_REPORTS_KEY, null);
JSONObject sentStatusReports;
try {
if (sentStatusReportsString == null) {
sentStatusReports = new JSONObject();
} else {
sentStatusReports = new JSONObject(sentStatusReportsString);
}
sentStatusReports.put(appVersionOrPackageIdentifier, status);
settings.edit().putString(STATUS_REPORTS_KEY, sentStatusReports.toString()).commit();
} catch (JSONException e) {
throw new CodePushUnknownException("Unable to save new entry in SharedPreferences under " + STATUS_REPORTS_KEY + ".", e);
}
settings.edit().putString(LAST_DEPLOYMENT_REPORT_KEY, appVersionOrPackageIdentifier).commit();
}
private void removeFailedUpdates() {
@@ -340,19 +319,9 @@ public class CodePush {
}
private void rollbackPackage() {
try {
WritableMap failedPackage = codePushPackage.getCurrentPackage();
saveFailedUpdate(failedPackage);
} catch (IOException e) {
throw new CodePushUnknownException("Attempted a rollback without having a current downloaded package", e);
}
try {
codePushPackage.rollbackPackage();
} catch (IOException e) {
throw new CodePushUnknownException("Error in rolling back package", e);
}
WritableMap failedPackage = codePushPackage.getCurrentPackage();
saveFailedUpdate(failedPackage);
codePushPackage.rollbackPackage();
removePendingUpdate();
}
@@ -464,23 +433,17 @@ public class CodePush {
AsyncTask asyncTask = new AsyncTask() {
@Override
protected Void doInBackground(Object... params) {
try {
WritableMap currentPackage = codePushPackage.getCurrentPackage();
WritableMap currentPackage = codePushPackage.getCurrentPackage();
Boolean isPendingUpdate = false;
Boolean isPendingUpdate = false;
if (currentPackage.hasKey(codePushPackage.PACKAGE_HASH_KEY)) {
String currentHash = currentPackage.getString(codePushPackage.PACKAGE_HASH_KEY);
isPendingUpdate = CodePush.this.isPendingUpdate(currentHash);
}
currentPackage.putBoolean("isPending", isPendingUpdate);
promise.resolve(currentPackage);
} catch (IOException e) {
e.printStackTrace();
promise.reject(e.getMessage());
if (currentPackage.hasKey(codePushPackage.PACKAGE_HASH_KEY)) {
String currentHash = currentPackage.getString(codePushPackage.PACKAGE_HASH_KEY);
isPendingUpdate = CodePush.this.isPendingUpdate(currentHash);
}
currentPackage.putBoolean("isPending", isPendingUpdate);
promise.resolve(currentPackage);
return null;
}
};
@@ -490,8 +453,8 @@ public class CodePush {
@ReactMethod
public void getNewStatusReport(Promise promise) {
// Check if there was a rollback that was not yet reported
if (didRollback) {
// Check if there was a rollback that was not yet reported
JSONArray failedUpdates = getFailedUpdates();
if (failedUpdates != null && failedUpdates.length() > 0) {
try {
@@ -499,7 +462,7 @@ public class CodePush {
WritableMap lastFailedPackage = CodePushUtils.convertJsonObjectToWriteable(lastFailedPackageJSON);
String lastFailedPackageIdentifier = getPackageStatusReportIdentifier(lastFailedPackage);
if (lastFailedPackage != null && isDeploymentStatusNotYetReported(lastFailedPackageIdentifier)) {
recordDeploymentStatusReported(lastFailedPackageIdentifier, DEPLOYMENT_FAILED_STATUS);
recordDeploymentStatusReported(lastFailedPackageIdentifier);
WritableNativeMap reportMap = new WritableNativeMap();
reportMap.putMap("package", lastFailedPackage);
reportMap.putString("status", DEPLOYMENT_FAILED_STATUS);
@@ -510,36 +473,34 @@ public class CodePush {
throw new CodePushUnknownException("Unable to read failed updates information stored in SharedPreferences.", e);
}
}
}
// Check if the current CodePush package has been reported
if (didUpdate) {
try {
WritableMap currentPackage = codePushPackage.getCurrentPackage();
if (currentPackage != null) {
String currentPackageIdentifier = getPackageStatusReportIdentifier(currentPackage);
if (currentPackageIdentifier != null && isDeploymentStatusNotYetReported(currentPackageIdentifier)) {
recordDeploymentStatusReported(currentPackageIdentifier, DEPLOYMENT_SUCCEEDED_STATUS);
WritableNativeMap reportMap = new WritableNativeMap();
reportMap.putMap("package", currentPackage);
reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS);
promise.resolve(reportMap);
return;
}
} else if (didUpdate) {
// Check if the current CodePush package has been reported
WritableMap currentPackage = codePushPackage.getCurrentPackage();
if (currentPackage != null) {
String currentPackageIdentifier = getPackageStatusReportIdentifier(currentPackage);
if (currentPackageIdentifier != null && isDeploymentStatusNotYetReported(currentPackageIdentifier)) {
recordDeploymentStatusReported(currentPackageIdentifier);
WritableNativeMap reportMap = new WritableNativeMap();
reportMap.putMap("package", currentPackage);
reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS);
promise.resolve(reportMap);
return;
}
}
} else {
String currentPackageHash = null;
currentPackageHash = codePushPackage.getCurrentPackageHash();
if (currentPackageHash == null) {
// Check if the current appVersion has been reported.
String binaryIdentifier = "" + getBinaryResourcesModifiedTime();
if (isDeploymentStatusNotYetReported(binaryIdentifier)) {
recordDeploymentStatusReported(binaryIdentifier);
WritableNativeMap reportMap = new WritableNativeMap();
reportMap.putString("appVersion", appVersion);
promise.resolve(reportMap);
return;
}
} catch (IOException e) {
// If didUpdate is true, there should be a current package, so this should not happen.
throw new CodePushUnknownException("Error getting current package after an update.", e);
}
}
// Check if the current appVersion has been reported.
if (isDeploymentStatusNotYetReported(appVersion)) {
recordDeploymentStatusReported(appVersion, DEPLOYMENT_SUCCEEDED_STATUS);
WritableNativeMap reportMap = new WritableNativeMap();
reportMap.putString("appVersion", appVersion);
promise.resolve(reportMap);
return;
}
promise.resolve("");
@@ -600,16 +561,11 @@ public class CodePush {
@ReactMethod
public void isFirstRun(String packageHash, Promise promise) {
try {
boolean isFirstRun = didUpdate
&& packageHash != null
&& packageHash.length() > 0
&& packageHash.equals(codePushPackage.getCurrentPackageHash());
promise.resolve(isFirstRun);
} catch (IOException e) {
e.printStackTrace();
promise.reject(e.getMessage());
}
boolean isFirstRun = didUpdate
&& packageHash != null
&& packageHash.length() > 0
&& packageHash.equals(codePushPackage.getCurrentPackageHash());
promise.resolve(isFirstRun);
}
@ReactMethod

View File

@@ -50,20 +50,28 @@ public class CodePushPackage {
return CodePushUtils.appendPathComponent(getCodePushPath(), STATUS_FILE);
}
public WritableMap getCurrentPackageInfo() throws IOException {
public WritableMap getCurrentPackageInfo() {
String statusFilePath = getStatusFilePath();
if (!CodePushUtils.fileAtPathExists(statusFilePath)) {
return new WritableNativeMap();
}
return CodePushUtils.getWritableMapFromFile(statusFilePath);
try {
return CodePushUtils.getWritableMapFromFile(statusFilePath);
} catch (IOException e) {
throw new CodePushUnknownException("Error getting current package info" , e);
}
}
public void updateCurrentPackageInfo(ReadableMap packageInfo) throws IOException {
CodePushUtils.writeReadableMapToFile(packageInfo, getStatusFilePath());
public void updateCurrentPackageInfo(ReadableMap packageInfo) {
try {
CodePushUtils.writeReadableMapToFile(packageInfo, getStatusFilePath());
} catch (IOException e) {
throw new CodePushUnknownException("Error updating current package info" , e);
}
}
public String getCurrentPackageFolderPath() throws IOException {
public String getCurrentPackageFolderPath() {
WritableMap info = getCurrentPackageInfo();
String packageHash = CodePushUtils.tryGetString(info, CURRENT_PACKAGE_KEY);
if (packageHash == null) {
@@ -73,7 +81,7 @@ public class CodePushPackage {
return getPackageFolderPath(packageHash);
}
public String getCurrentPackageBundlePath() throws IOException {
public String getCurrentPackageBundlePath() {
String packageFolder = getCurrentPackageFolderPath();
if (packageFolder == null) {
return null;
@@ -86,17 +94,17 @@ public class CodePushPackage {
return CodePushUtils.appendPathComponent(getCodePushPath(), packageHash);
}
public String getCurrentPackageHash() throws IOException {
public String getCurrentPackageHash() {
WritableMap info = getCurrentPackageInfo();
return CodePushUtils.tryGetString(info, CURRENT_PACKAGE_KEY);
}
public String getPreviousPackageHash() throws IOException {
public String getPreviousPackageHash() {
WritableMap info = getCurrentPackageInfo();
return CodePushUtils.tryGetString(info, PREVIOUS_PACKAGE_KEY);
}
public WritableMap getCurrentPackage() throws IOException {
public WritableMap getCurrentPackage() {
String folderPath = getCurrentPackageFolderPath();
if (folderPath == null) {
return new WritableNativeMap();
@@ -111,7 +119,7 @@ public class CodePushPackage {
}
}
public WritableMap getPackage(String packageHash) throws IOException {
public WritableMap getPackage(String packageHash) {
String folderPath = getPackageFolderPath(packageHash);
String packageFilePath = CodePushUtils.appendPathComponent(folderPath, PACKAGE_FILE_NAME);
try {
@@ -185,7 +193,7 @@ public class CodePushPackage {
updateCurrentPackageInfo(info);
}
public void rollbackPackage() throws IOException {
public void rollbackPackage() {
WritableMap info = getCurrentPackageInfo();
String currentPackageFolderPath = getCurrentPackageFolderPath();
CodePushUtils.deleteDirectoryAtPath(currentPackageFolderPath);

View File

@@ -31,6 +31,8 @@ module.exports = {
};
function getHttpMethodName(verb) {
// Note: This should stay in sync with the enum definition in
// https://github.com/Microsoft/code-push/blob/master/sdk/script/acquisition-sdk.ts#L6
return [
"GET",
"HEAD",