Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rollback always in production #1096

Closed
Fuhrmann opened this issue Dec 4, 2017 · 3 comments
Closed

Rollback always in production #1096

Fuhrmann opened this issue Dec 4, 2017 · 3 comments

Comments

@Fuhrmann
Copy link

Fuhrmann commented Dec 4, 2017

Hello! I'm using react-native-navigation and maybe it has something to do with it, so I'll inform this first.

This is my package.json:

...
...
"babel-preset-react-native-stage-0": "^1.0.1",
"react-native": "^0.49.3",
"appcenter": "^1.0.1",
"appcenter-analytics": "^1.0.1",
"appcenter-crashes": "^1.0.1",
"react-native-code-push": "^5.2.0-beta",
"react-native-navigation": "^1.1.295",
...
...

My .babelrc is configured correctly so I can use the decorator:

...
"presets": ["react-native-stage-0/decorator-support"],
...

My index.js file:

import App from './App/Containers/App';
import './App/Config/ReactotronConfig';

new App();

My App/Containers/App file:

import 'Config';
import createStore from 'App/Redux';

export default class App {
  constructor() {
    createStore();
  }
}

I've already searched in all issues and found a related issue: #875. I've followed the steps and created a custom component that returns null just to make codepush work:

import React from 'react';
import codePush from 'react-native-code-push';

@codePush({
  installMode: codePush.InstallMode.ON_NEXT_RESTART,
  mandatoryInstallMode: codePush.InstallMode.ON_NEXT_RESTART,
})
export default class CodePushComponent extends React.Component {
  render() {
    return null;
  }
}

So, I render in my DashboardScreen (the first screen that appears when the user opens the app):

import CodePushComponent from 'App/Components/CodePush';
...
...
render() {
   ...
   ...
   <CodePushComponent />
   ...
   ...
}

The DashboardScreen is being registered this way:

Navigation.registerComponent(screens.DASHBOARD_SCREEN, () => screenWrapper(DashboardScreen), store, Provider);

The screenWrapper is just a HOC to apply some default navigation styles that is used in the react-native-navigation package:

import React from 'react';
import defaultNavigationStyles from './DefaultNavigationStyle';

export default function screenWrapper(WrappedComponent) {
  const wrapper = class extends React.Component {
    static navigatorStyle = {
      ...defaultNavigationStyles,
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };


  if (WrappedComponent.navigatorStyle) {
    wrapper.navigatorStyle = Object.assign(wrapper.navigatorStyle, WrappedComponent.navigatorStyle);
  }


  if (WrappedComponent.navigatorButtons) {
    wrapper.navigatorButtons = WrappedComponent.navigatorButtons;
  }

  return wrapper;
}

This is my MainApplication.java:

package com.myapp;

import com.RNFetchBlob.RNFetchBlobPackage;
import com.airbnb.android.react.maps.MapsPackage;
import com.facebook.react.ReactPackage;
import com.lugg.ReactNativeConfig.ReactNativeConfigPackage;
import com.lwansbrough.RCTCamera.RCTCameraPackage;
import com.microsoft.appcenter.reactnative.analytics.AppCenterReactNativeAnalyticsPackage;
import com.microsoft.appcenter.reactnative.appcenter.AppCenterReactNativePackage;
import com.microsoft.appcenter.reactnative.crashes.AppCenterReactNativeCrashesPackage;
import com.microsoft.codepush.react.CodePush;
import com.oblador.vectoricons.VectorIconsPackage;
import com.reactnative.ivpusic.imagepicker.PickerPackage;
import com.reactnativenavigation.NavigationApplication;
import com.myapp.modules.BackupData.RNMyAppPackage;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends NavigationApplication {

    @Override
    public boolean isDebug() {
        return BuildConfig.DEBUG;
    }

    protected List<ReactPackage> getPackages() {
        return Arrays.asList(
                new AppCenterReactNativeCrashesPackage(MainApplication.this, getResources().getString(R.string.appcenterCrashes_whenToSendCrashes)),
                new AppCenterReactNativeAnalyticsPackage(MainApplication.this, getResources().getString(R.string.appcenterAnalytics_whenToEnableAnalytics)),
                new AppCenterReactNativePackage(MainApplication.this),
                new CodePush(BuildConfig.CODEPUSH_DEPLOYMENT_KEY, MainApplication.this, BuildConfig.DEBUG),
                new RNMyAppPackage(),
                new VectorIconsPackage(),
                new ReactNativeConfigPackage(),
                new RNFetchBlobPackage(),
                new RCTCameraPackage(),
                new PickerPackage(),
                new MapsPackage()
        );
    }

    @Override
    public List<ReactPackage> createAdditionalReactPackages() {
        return getPackages();
    }

    @Override
    public String getJSMainModuleName() {
        return "index";
    }

    @Override
    public String getJSBundleFile() {
        return CodePush.getJSBundleFile();
    }

    @Override
    public String getBundleAssetName() {
        return "index.android.bundle";
    }
}

My deployment key is specified in a .env.prod file because I'm using react-native-conf. This file is setup correctly when building from appcenter using a appcenter-pre-build.hs build script. This is my .env.prod file:

image

My application is being built using AppCenter:

image
(the build is fine)

While building, the pre build script create the .env.prod file and apply the codepush api key:

image

I download the APK and install on my simulator and physical device.

When I open the app, the bundle is loded correcly from CodePush:

12-04 07:56:43.372 6351-6351/com.myapp D/ReactNative: [CodePush] Loading JS bundle from "assets://index.android.bundle"

Right before the DashboardScreen is open, I got the following log:

12-04 08:10:19.519 7013-7037/com.myapp I/ReactNativeJS: [CodePush] Checking for update.
12-04 08:10:19.520 7013-7037/com.myapp I/ReactNativeJS: [CodePush] Reporting binary update (0.0.1)
12-04 08:10:20.715 7013-7037/com.myapp I/ReactNativeJS: [CodePush] Downloading package.
12-04 08:10:22.982 7013-7036/com.myapp D/ReactNative: [CodePush] Applying full update.
12-04 08:10:22.983 7013-7037/com.myapp I/ReactNativeJS: [CodePush] Installing update.
12-04 08:10:23.034 7013-7038/com.myapp D/ReactNative: [CodePush] Loading JS bundle from "/data/user/0/com.myapp/files/CodePush/166f00235674bfc21c3234bc826338caa2982f204c676255071b1ff4d97246b5/code-push117113-8356-94oipj.96vm/index.android.bundle"
12-04 08:10:23.035 7013-7037/com.myapp I/ReactNativeJS: [CodePush] Restarting app

So the bundle is downloaded, applied, but when the app is restarted by codepush, it crashes:

image

After the crash, I try to open the app again and the following log is received:

12-04 08:11:45.880 7173-7173/com.myapp D/ReactNative: [CodePush] Update did not finish loading the last time, rolling back to a previous version.
12-04 08:11:45.892 7173-7173/com.myapp D/ReactNative: [CodePush] Loading JS bundle from "assets://index.android.bundle"
12-04 08:11:46.748 7173-7198/com.myapp I/ReactNativeJS: [CodePush] Sync already in progress.
12-04 08:11:46.773 7173-7198/com.myapp I/ReactNativeJS: [CodePush] Checking for update.
12-04 08:11:46.776 7173-7198/com.myapp I/ReactNativeJS: [CodePush] Reporting CodePush update rollback (v5)
12-04 08:11:48.089 7173-7198/com.myapp I/ReactNativeJS: [CodePush] An update is available, but it is being ignored due to having been previously rolled back.
12-04 08:11:48.090 7173-7198/com.myapp I/ReactNativeJS: [CodePush] App is up to date.

Then in AppCenter I got the rollback results:

image

So the updated did not complete. I guess the notifyAppReady maybe is not being called?

Steps to Reproduce

I'm using inside my application, so I dont have any steps to reproduce to inform here.

Expected Behavior

I expect that the update is installed on the user's phone.

Actual Behavior

What actually happens?
The application try to update, download the bundle, restarts and rollback.

Environment

  • react-native-code-push version: ^5.2.0-beta
  • react-native version: ^0.49.3
  • iOS/Android/Windows version: Android 7.1.1
  • Does this reproduce on a debug build or release build? Release always. Debug works fine.
  • Does this reproduce on a simulator, or only on a physical device? Simulator and physical device.

If I can provide any other information tell me. Thanks!

@Fuhrmann
Copy link
Author

Fuhrmann commented Dec 4, 2017

Okay, after a few tries I found the problem and a workaround but not a solution. Inside my application I have a backup service that runs in background on Android. I declare a new React Native package:

package com.myapp.modules.BackupData;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RNMyAppPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new BackupDataModule(reactContext));
        return modules;
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

And in my BackupModule I start the service:

package com.myapp.modules.BackupData;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

public class BackupDataModule extends ReactContextBaseJavaModule {
    public BackupDataModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "BackupDataModule";
    }

    @ReactMethod
    public void startService() {
        BackupDataService.scheduleAlarm(getReactApplicationContext());
    }
}

In my BackupDataService I start a new Intent and set to run inexactRepeating using the AlarmManager in Android:

Intent intent = new Intent(context, BackupDataReceiver.class);
intent.setAction("com.myapp.backupdata");
...
...
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(
        AlarmManager.RTC_WAKEUP,
        calendar.getTimeInMillis(),
        AlarmManager.INTERVAL_HALF_DAY,
        myIntent

Inside my application javascript code I import my module and start the service:

import BackupDataService from 'App/Services/BackupDataService';
...
...
...
BackupDataService.startService();
...
...

After I removed the call to my background service, rebuild the apk, install on the emulator, open the application, restart the aplication, the update was applied correctly, as you can see in the logs:

2-04 08:47:59.025 8857-8969/com.myapp I/ReactNativeJS: [CodePush] Checking for update.
12-04 08:47:59.031 8857-8969/com.myapp I/ReactNativeJS: [CodePush] Reporting CodePush update success (v6)
12-04 08:48:00.114 8857-8969/com.myapp I/ReactNativeJS: [CodePush] App is up to date.

Could be this error related to the fact that I don't declare my the ReactInstanceHolder as the docs instruct to do so? The docs says:

his section is only necessary if you're explicitly launching a React Native instance without an Activity (for example, from within a native push notification receiver). For these situations, CodePush must be told how to find your React Native instance.

But using the react-native-navigation package, my MainApplication.java is extending NavigationApplication.java and I don't know how to declare the ReactInstanceHolder.

@ruslan-bikkinin
Copy link
Contributor

Hi @Fuhrmann and sorry for delayed response. Yes, you problem might be related with fact that you didn't declared ReactInstanceHolder. To declare ReactInstanceHolder you just need to edit MainApplication.java as following:

import com.microsoft.codepush.react.ReactInstanceHolder;

//implement ReactInstanceHolder interface
public class MainApplication extends NavigationApplication implements ReactInstanceHolder {

    ///...
    @Override
    public ReactInstanceManager getReactInstanceManager() {
        return getReactNativeHost().getReactInstanceManager();
    }
}

Please let us know if it solves your problem or any updates.

@Fuhrmann
Copy link
Author

Fuhrmann commented Dec 13, 2017

Hi! You are right. That was the problem. Thanks so much for the help, I'll close since we solved it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants