React Native - Automatic version name from package.json to android build manifest

react-native cfbundleshortversionstring
react-native version
react native android increment version
react native android release version
react native android version
change build version react native android
get version number from package json react native
react native bump version

Currently I have a react native app and the issue that I have is that is very time consuming to update the version on every build or commit.

Also, I have Sentry enabled so every time I build, some builds get the same version so some crashes are hard to determine where they came from.

Lastly, updating the version manually is error prone.

How can I setup my builds to generate an automatic version every time I build and forget about all of this manual task?

Since I was working with this for several days, I decided to share with everyone how I did it, because it could help others.

Tools used:

  1. GitVersion: We will use GitVersion to generate a semantic version automatically depending on many factors like current branch, tags, commits, etc. The toold does an excellent job and you can forget about naming your versions. Of course, if you set a tag to a commit, it will use that tag as name.
  2. PowerShell: This command line OS built by Microsoft has the ability to be run from Mac, Linux or Windows, and I chose it because the builds can be agnostic of the OS version. For example I develop on Windows but the build machine has MacOS.
Edit App build.gradle

The app gradle only needs one line added at the end of it. In my case I have the Google Play Services gradle and I added it after that.

apply from: 'version.gradle'
version.gradle

This file should be in the same folder as your app gradle and this is the content:

task updatePackage(type: Exec, description: 'Updating package.json') {
    commandLine 'powershell', ' -command ' , '$semver=(gitversion /showvariable Semver); Set-Content -path version.properties -value semver=$semver; npm version --no-git-tag-version --allow-same-version $semver'  
}
preBuild.dependsOn updatePackage

task setVariantVersion {

    doLast {

        if (plugins.hasPlugin('android') || plugins.hasPlugin('android-library')) {

            def autoIncrementVariant = { variant ->
                variant.mergedFlavor.versionName = calculateVersionName()
            }

            if (plugins.hasPlugin('android')){
                //Fails without putting android. first
                android.applicationVariants.all { variant -> autoIncrementVariant(variant) }
            }

            if (plugins.hasPlugin('android-library')) {
                //Probably needs android-library before libraryVariants. Needs testing
                libraryVariants.all { variant -> autoIncrementVariant(variant) }
            }
        }

    }

}
preBuild.dependsOn setVariantVersion
setVariantVersion.mustRunAfter updatePackage

ext {
    versionFile = new File('version.properties')
    calculateVersionName = {
        def version = readVersion()
        def semver = "Unknown"
        if (version != null){
            semver = version.getProperty('semver')
        }
        return semver
    }
}

Properties readVersion() {
    //It gets called once for every variant but all get the same version
    def version = new Properties()
    try {
        file(versionFile).withInputStream { version.load(it) }
    } catch (Exception error) {
        version = null
    } 
    return version
}

Now, let's review what the script is actually doing:

  1. updatePackage: This task runs at the very beginning of your build (actually before preBuild) and it executes gitversion to get the current version and then creates a version.properties file which later be read by gradle to take the version.
  2. setVariantVersion: This is called afterEvaluate on every variant. Meaning that if you have multiple builds like debug, release, qa, staging, etc, all will get the same version. For my use case this is fine, but you might want to tweak this.
  3. Task Order: One thing that bothered me was that the version was being run before the file was generated. This is fixed by using the mustRunAfter tag.
PowerShell Script Explained

This is the script that gets run first. Let's review what is doing:

$semver=(gitversion /showvariable Semver);
Set-Content -path props.properties -value semver=$semver; 
npm version --no-git-tag-version --allow-same-version $semver
  1. Line 1: gitversion has multiple type of versions. If you run it without any parameter you will get a json file with many variants. Here we are saying that we only want the SemVer. (See also FullSemVer)
  2. Line 2: PowerShell way to create a file and save the contents to it. This can be also made with > but I had encoding issues and the properties file was not being read.
  3. Line 3: This line updates your package.json version. By default it saves a commit to git with the new version. --no-git-tag-version makes sure you don't override it.

And that is it. Now every time you make a build, the version should be generated automatically, your package.json updated and your build should have that specific version name.

App Center

Since I am using App Center to make the builds, I will tell you how you can use this in a Build machine. You only need to use a custom script.

app-center-pre-build.sh
#!/usr/bin/env sh
#Installing GitVersion
OS=$(uname -s)
if [[ $OS == *"W64"* ]]; then
    echo "Installing GitVersion with Choco"
    choco install GitVersion.Portable -y
else 
    echo "Installing GitVersion with Homebrew"
    brew install --ignore-dependencies gitversion
fi

This is needed because GitVersion is not currently a part of the build machines. Also, you need to ignore the mono dependency when installing, otherwise you get an error when brew tries to link the files.

React Native - Automatic version name from package - android, React Native - Automatic version name from package.json to android build manifest - android. The first two plugins are for handling the version, version code on android and the third one is for reading a JSON file (our package.json). Next, we are going to add our fastlane scripts. Copy the following to the fastfile at fastlane/Fastfile .

While the currently accepted answer will work, there is a much simpler, and therefore more reliable way to do it. You can actually read the value set in package.json right from build.gradle.

Modify your android/app/build.gradle:

// On top of your file import a JSON parser
import groovy.json.JsonSlurper

// Create an easy to use function
def getVersionFromNpm() {
    //  Read and parse package.json file from project root
    def inputFile = new File("$rootDir/../package.json")
    def packageJson = new JsonSlurper().parseText(inputFile.text)

    // Return the version, you can get any value this way
    return packageJson["version"]
}

android {
    defaultConfig {
        applicationId "your.app.id"
        versionName getVersionFromNpm()
    }
}

This way you won't need a pre-build script or anything, it will just work.

APSL/react-native-version-number: Gets the version , Gets the version number and build number of your app. and bundleIdentifier on IOS. For Android, returns the versionName , versionCode and applicationId . Moving package.json into the src folder works to get the version, but then I can't run any of the npm commands to actually build and run the app. – Baldeep Aug 31 '17 at 10:01 1 @shaochuancs yes, turns out the app was created using react-create-app which puts a restriction through webpack.

The @MacRusher version was fine for me. Just for further readers, I had to add .toInteger() to make it work. Since I'm using yarn version --patch to automatically upgrade the version in pacakge.json I also had to take only the two first characters.

Here is the new version:

// On top of your file import a JSON parser
import groovy.json.JsonSlurper

def getVersionFromPackageJson() {
    //  Read and parse package.json file from project root
    def inputFile = new File("$rootDir/../package.json")
    def packageJson = new JsonSlurper().parseText(inputFile.text)

    // Return the version, you can get any value this way
    return packageJson["version"].substring(0,2).toInteger()
}

android {
    defaultConfig {
        applicationId "your.app.id"
        versionName getVersionFromPackageJson()
    }
}

react-native-set-version, Set the version in package.json, android/app/build.gradle and It will update the version name and the version code in both build.gradle For that reason react-​native-set-version will only write in the AndroidManifest.xml if  Then, Rename your app $ npx react-native-rename "Travel App" With custom Bundle Identifier $ npx react-native-rename "Travel App" -b com.junedomingo.travelapp After you change the name, please make sure you go to the android folder and run gradlew clean. then go back to the main project and run npx react-native run-android.

Versioning React Native apps - Andrew Jack, React Native brings together JavaScript, Android and iOS. With that comes three different build tools, npm, Xcode, and Gradle. How you use the package.json version to calculate the versionCode and versionName is up to  Every app project must have an AndroidManifest.xml file (with precisely that name) at the root of the project source set. The manifest file describes essential information about your app to the Android build tools, the Android operating system, and Google Play.

Getting Started with React Native, Make sure your fbt has a version greater than "^0.10.0". Create a Add "locale|​layoutDirection" in android:configChanges in the AndroidManifest.xml. This will restart Name the files Localizable.strings as this is what the native module will look for when looking for translations Add the manifest script call in package.​json. Here's what the fields in the JSON mean: version — this is the version of the bundle file (in major.minor.patch format); minContainerVersion — this is the minimum version of the container (native) app that is allowed to download this bundle (this is needed because adding a new React Native component to your app might result into changed native app, hence, going through the AppStore review

Creating a React App, Use create-react-app to bootstrap a React application on your own computer. Then, run create-react-app with the name you would like to use for your ~1.2.3 in package.json, which will only install the most recent minor version matching 1.2.x. Browsing this folder, you see the main App JavaScript component (App.js​),  Also, we can see that in package.json, the single build dependency react-scripts is removed from our tutorial project and all the individual dependencies are listed. However, please be aware that running eject is an irreversible step or action. i.e. we cannot revert or go back to the initial state after running this command.

Comments
  • This is a nice addition. Keep in mind this would not replace the whole script because art of the "magic" is that it automatically updates the package.json version automatically. But this would remove the need for the intermediate file, which I like. Thanks!
  • I just remember and this is different towhat I did. Your script requires the user to manually update the version in the package.json. What I did it is meant to be run and GitVersion will calculate the version for you. It is mostly for people that have CD/CI deployments.
  • You're right, my code snippet just reads the version from package.json. I thought that this might be useful for many people (I stumble on your question myself while I was looking for such solution) as actually setting the version differs very much depending on project and use case. Some may use npm version command, other semantic-release or GitVersion like you.