Categories:
Loading... Search articles

Search for articles

Sorry, but we couldn't find any matches...

But perhaps we can interest you in one of our more popular articles?
Getting started guide to build React Native apps with Codemagic CI/CD

Continuous Integration and Delivery for React Native apps

Jul 15, 2020

This is a getting started guide to build React Native apps with Codemagic CI/CD.

Codemagic is now building more than just Flutter apps, meaning that anyone with an existing React Native app can use Codemagic to run their builds. But how do you get started with something like that? Lewis Cianci checks it out.

When it comes to CI/CD, Codemagic has a lot of exciting things to offer to a developer (like a easy project setup, a really great Slack community, and remote access to the build boxes). But now, we’ve also got workflows on Codemagic for React Native — and this is amazing.

Top reasons to use Codemagic CI/CD for React Native apps

Easy project setup

Getting started with building React Native apps is easy thanks to ready-made configuration templates. Simply select React Native App as the starter workflow and kick off your project.

Customizable workflows for iOS and Android

Codemagic was built with cross-platform app development in mind. Have full control over your React Native pipeline with workflows for iOS and Android that fit the needs of your development team.

Seamless code signing for iOS and Android

Simply upload your Android keystore and iOS code signing files to receive signed artifacts. With automatic code signing for iOS, Codemagic can take care of creating and managing signing certificates and provisioning profiles for you.

Unit and integration tests

Assure the quality of your React Native apps with automated tests. Run unit tests with Jest or test the full app on the simulator/emulator or real devices with Appium or Detox.

Distribute React Native apps with ease

Distribute the latest version of your app to testers on TestFlight, Google Play or Firebase App Distribution, or configure your workflows to automatically publish to the Apple App Store or Google Play.

First-class support

Codemagic Slack workspace community has more than 3,000 members. Find quick solutions and discuss your projects with fellow developers.

Getting started with Codemagic CI/CD for React Native apps

After signing up, you can use React Native workflows by creating a new project in Codemagic and simply selecting the React Native App from the options.

That’s our cue, on the left!
That’s our cue, on the left!

Clicking on it will open up the “Getting started guide” for React Native:

Hitting Next shows us our codemagic.yaml file.

We can download this and add it to our local repository. This configures our Codemagic pipelines, so Codemagic knows how to run our React Native build on its build servers.

Modifying the YAML file for our build

The next step in this process is to modify our codemagic.yaml file to get it to build our solution, so let’s see what this file looks like:

# Check out https://docs.codemagic.io/getting-started/building-a-react-native-app/ for more information
# Please review and update values in curly braces

workflows:
  react-native:
    name: React Native App
    environment:
      vars:
        XCODE_WORKSPACE: '{{ ADD WORKSPACE NAME HERE }}'
        XCODE_SCHEME: '{{ ADD SCHEME NAME HERE }}'
      node: latest
    scripts:
      - npm install
      - echo "sdk.dir=$HOME/programs/android-sdk-macosx" > "$FCI_BUILD_DIR/android/local.properties"
      - |
        # build Android
        cd android
        ./gradlew assembleDebug        
      - |
        # build iOS
        cd ios
        pod install
        xcodebuild build -workspace "$XCODE_WORKSPACE.xcworkspace" -scheme "$XCODE_SCHEME" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO        
    artifacts:
      - android/app/build/outputs/**/**/*.apk
      - $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.app

Clearly, there are some changes that we need to make to this YAML to get a successful build.

The required changes

Android

Broadly speaking, you almost never want to build a debug version of your app in your CI/CD pipeline. We need to tell the build process to build a release build instead. To do this, we just update the lines under #build Android to look more like this:

- |
  # build Android
  cd android
  ./gradlew assembleRelease  

The build process will now follow the path as defined in our build.gradle. Code signing is a topic in itself (covered in depth here), so we’ll just describe the necessary steps to get the build to work.

In your App level build.gradle (that’s under YourProjectDirectory\android\app), we need to add in our release signing details. But we need to use the release signing details only when our build is being run on Codemagic, and not locally.

We then configure the build process to read the signing details from the release file.

signingConfigs {
    debug {
        storeFile file('debug.keystore')
        storePassword 'android'
        keyAlias 'androiddebugkey'
        keyPassword 'android'
    }

    release {
        if (System.getenv()['CI']) { // CI=true is exported by Codemagic
            storeFile file(System.getenv()['FCI_BUILD_DIR'] + '/keystore.jks')
            storePassword System.getenv()['FCI_KEYSTORE_PASSWORD']
            keyAlias System.getenv()['FCI_KEY_ALIAS']
            keyPassword System.getenv()['FCI_KEY_PASSWORD']
        }
        else {
            def keystorePropertiesFile = rootProject.file('key.properties')
            def keystoreProperties = new Properties()
            keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
        }
    }
}

What’s actually happening here? In a nutshell, if we are running in a release build, we check if the CI environment variable is declared (Codemagic build machines declare this variable). If we are, then we fetch the location of the keystore and appropriate signing details from the environment variables. Otherwise, we use the local key.properties for these details, and fetch the details from there instead. This means that our builds continue to run not only locally but also on Codemagic, which is a nice bonus.

Our local debug key.properties might look something like this (this is for a debug keystore, so yours will be different):

storePassword=android
keyPassword=yourpassword
keyAlias=androiddebugkey
storeFile=keystore.keystore

These are just the keys for the debug keystore, but normally, these are incredibly sensitive details. We don’t want just anyone to be able to read these settings, so checking these files into source control is a really bad idea. Instead, we will encrypt them in Codemagic and have the build process decrypt the required details just before the build runs.

Encrypting our build time dependencies

On our project configuration page, we can use the Encrypt environment variables button to convert sensitive details into a format that can be securely checked in.

Next, we can drag our keystore file into this window and receive an encrypted variable containing our keystore. We just click the little clipboard next to the icon to copy the encrypted keystore variable to our clipboard.

We can also use this screen to encrypt individual variables, like the keystore alias and password. We’ll do this now, and paste these values into our codemagic.yaml file. When we’re done, it should look a little bit like this:

We’ve configured our build variables, so we can now use them in our build process later on. With these four environment variables encrypted (the keystore, the keystore password, the key alias and password), we’re good to go.

Build script

For our Android builds, we want to:

  1. Make the cloned directory writable (I was getting build failures without this, hence the chmod)
  2. Write the keystore file into our repository so it can be used by the build process
  3. Switch to the Android directory and run the assembleRelease gradle task, which will produce a signed APK.

In the codemagic.yaml file, it looks like this:

- npm install
- echo "sdk.dir=$HOME/programs/android-sdk-macosx" > "$FCI_BUILD_DIR/android/local.properties"
- |
  chmod -R 777 $FCI_BUILD_DIR
  echo $CM_KEYSTORE | base64 --decode > $FCI_BUILD_DIR/keystore.jks
  # build Android
  cd android
  ./gradlew assembleRelease  

For Android, it’s really that easy! For iOS, it’s a little more involved …

iOS

In order for our builds to complete successfully, we need to load both our certificate and the .mobileprovision file from App Store Connect onto the build agent. In this example I have encrypted the signing certificate as CM_CERTIFICATE, the password for the certificate as CM_CERTIFICATE_PASSWORD and the provisioning profile as CM_PROVISIONING_PROFILE.

Build script

This looks a bit more complicated than the Android equivalent, and also uses ✨ fancy Codemagic CLI functionality ✨to make it work. Here’s what we’re doing in this build:

  1. Running keychain initialize to prepare for the import of our signing certificate into the build agent
  2. Setting PROFILES_HOME to where our mobile provisioning certificate is about to be, and then creating that directory
  3. Setting PROFILE_PATH to a temporary location within PROFILES_HOME
  4. Writing our PROVISIONING_PROFILE to PROFILE_PATH, with a temporary name
  5. Writing our signing certificate (CM_CERTIFICATE) to /tmp/certificate.p12
  6. Adding this certificate to the build box’s local certificate store, using CM_CERTIFICATE_PASSWORD to do so
  7. Running the xcode-project use-profiles command
  8. And then just basically changing into the iOS directory, running pod install, and using xcode-project build-ipa to build our app, while specifying a workspace and scheme to use
- keychain initialize
  - |
    # set up provisioning profiles
    PROFILES_HOME="$HOME/Library/MobileDevice/Provisioning Profiles"
    mkdir -p "$PROFILES_HOME"
    PROFILE_PATH="$(mktemp "$PROFILES_HOME"/$(uuidgen).mobileprovision)"
    echo ${CM_PROVISIONING_PROFILE} | base64 --decode > "$PROFILE_PATH"
    echo "Saved provisioning profile $PROFILE_PATH"    
  - |
    # set up signing certificate
    echo $CM_CERTIFICATE | base64 --decode > /tmp/certificate.p12
    keychain add-certificates --certificate /tmp/certificate.p12 --certificate-password $CM_CERTIFICATE_PASSWORD    
  - xcode-project use-profiles
  - |
    # build iOS
    cd ios
    pod install    
  - xcode-project build-ipa --workspace "ios/$XCODE_WORKSPACE.xcworkspace" --scheme "$XCODE_SCHEME"

The finished codemagic.yaml

After all that, you should have a codemagic.yaml file that looks a bit like this:

workflows:
  react-native:
    name: React Native App
    environment:
      vars:
        XCODE_WORKSPACE: 'CodemagicTestProject2'
        XCODE_SCHEME: 'CodemagicTestProject2'
        CM_KEYSTORE: Encrypted(...)
        FCI_KEYSTORE_PASSWORD: Encrypted(...)
        FCI_KEY_PASSWORD: Encrypted(...)
        FCI_KEY_ALIAS: Encrypted(...)
        CM_CERTIFICATE: Encrypted(...)
        CM_CERTIFICATE_PASSWORD: Encrypted(...)
        CM_PROVISIONING_PROFILE: Encrypted(...)
      node: latest
    scripts:
      - npm install
      - echo "sdk.dir=$HOME/programs/android-sdk-macosx" > "$FCI_BUILD_DIR/android/local.properties"
      - |
        chmod -R 777 $FCI_BUILD_DIR
        echo $CM_KEYSTORE | base64 --decode > $FCI_BUILD_DIR/keystore.jks
        # build Android
        cd android
        ./gradlew assembleRelease        
      - keychain initialize
      - |
        # set up provisioning profiles
        PROFILES_HOME="$HOME/Library/MobileDevice/Provisioning Profiles"
        mkdir -p "$PROFILES_HOME"
        PROFILE_PATH="$(mktemp "$PROFILES_HOME"/$(uuidgen).mobileprovision)"
        echo ${CM_PROVISIONING_PROFILE} | base64 --decode > "$PROFILE_PATH"
        echo "Saved provisioning profile $PROFILE_PATH"        
      - |
        # set up signing certificate
        echo $CM_CERTIFICATE | base64 --decode > /tmp/certificate.p12
        keychain add-certificates --certificate /tmp/certificate.p12 --certificate-password $CM_CERTIFICATE_PASSWORD        
      - xcode-project use-profiles
      - |
        # build iOS
        cd ios
        pod install        
      - xcode-project build-ipa --workspace "ios/$XCODE_WORKSPACE.xcworkspace" --scheme "$XCODE_SCHEME"
    artifacts:
      - android/app/build/outputs/**/**/*.apk
      - build/ios/ipa

A small note - the Encrypted(...) are placeholder values. You’ll need to encrypt your own values on Codemagic that suit your build process.

And that’s it! Your React Native app should build with Codemagic now. If you have any questions or comments, be sure to drop into Codemagic’s Slack channel and let us know. Happy developing!


Lewis Cianci is a software developer in Brisbane, Australia. His first computer had a tape drive. He’s been developing software for at least ten years, and has used quite a few mobile development frameworks (like Ionic and Xamarin Forms) in his time. After converting to Flutter, though, he’s never going back. You can reach him at his blog, read about other non-fluttery things at Medium, or maybe catch a glimpse of him at your nearest and most fanciest coffee shop with him and his dear wife.

How did you like this article?

Oops, your feedback wasn't sent

Latest articles

Show more posts