Releasing your apps can be a repetitive, sometimes even annoying task. You prepared and built your app for the stores, only to find out you forgot to bump a version code. If you don't like to do this, the great people from Conventional Changelog got just the thing for you!
Standard version is a tool to simplify the release process with a single command, yarn standard-version
. It's customizable and should have everything you need, like generated changelogs from conventional commits. If you don't know the tool yet, check out the documentation first.
In this guide, I'll show you how to configure Standard version for Expo. We will set up an example project and configure it for automated expo.version
, expo.android.versionCode
and expo.ios.buildNumber
.
🚀 Let's get going
First things first, create an empty Expo project using the blank template.
$ expo init --template blank
Next, we need to define a starting version for our project. Standard version uses the package.json
's version
property to determine the current version. This value is used to calculate the next release. Let's set this property to 0.0.0
.
{
"version": "0.0.0",
"main": "node_modules/expo/AppEntry.js",
...
}
Now add Standard version, with the Expo extension, to our dev dependencies.
$ yarn add --dev standard-version@next standard-version-expo
Currently, standard-version@7.1.0 is the only version that supports updaters from packages. That's why we need
@next
here.
You can test the project by running standard-version in dry-run mode. This mode only tells what it will do without doing anything.
$ yarn standard-version --dry-run
If you get the
Invalid Version: undefined
error, it means thepackage.json
doesn't have a starting version.
⚙️ Configure Standard version
Now that we've set up the basics, we need to configure Standard version for Expo. Create a file named .versionrc.js
with:
module.exports = {
bumpFiles: [
{
filename: 'package.json',
},
{
filename: 'app.json',
updater: require.resolve('standard-version-expo'),
},
],
};
This tells Standard version that we want to update
app.json
, using the default updater from the Expo extension. The updater bumpsexpo.version
with the new version, so you don't have to! 😬Note: We also want to update our
package.json
, with the default updater, to keep a single source of truth for our versions. (See PR #3 for more details)
Test your configuration again with --dry-run
, if you see the following output, you did an excellent job. 🦄
$ yarn standard-version --dry-run
✔ bumping version in app.json from 1.0.0 to 0.0.1
✔ created CHANGELOG.md
✔ outputting changes to CHANGELOG.md
...
✔ committing app.json and CHANGELOG.md
✔ tagging release v0.0.1
Now commit these changes and create a new minor version.
$ git add .
$ git commit -m 'feat: add standard version for releases'
$ yarn standard-version --release-as minor
After running the tool, you should have a new CHANGELOG.md
and an updated app.json
file! You still need to push the local changes to remote, but that also gives you an option to revert changes if something went wrong. 🧑🔧
📱 Configure build versions
If you are planning on releasing your app to the stores, we need to configure Standard version a bit more. It also needs to update the Android version codes and iOS build numbers. Let's start by updating our manifest with iOS and Android configuration.
{
"expo": {
...
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.bycedric.awesomeapp",
"buildNumber": "0.0.0"
},
"android": {
"package": "com.bycedric.awesomeapp",
"versionCode": 0
}
}
}
Great, now we need to tell Standard version about these new properties. The values should change on every release, just like our expo.version
. To do this, open up .versionrc.js
and add the new version bumpers.
module.exports = {
bumpFiles: [
{
filename: 'package.json',
},
{
filename: 'app.json',
updater: require.resolve('standard-version-expo'),
},
{
filename: 'app.json',
updater: require.resolve('standard-version-expo/android'),
},
{
filename: 'app.json',
updater: require.resolve('standard-version-expo/ios'),
},
],
};
There are multiple types of version updaters, each with their own "tactic". You can find them all here. For simplicity, let's stick to the recommended bumpers for now.
That's it! If you commit these changes and run Standard version, it will update the build version numbers for you too. 🌈
$ git add .
$ git commit -m 'feat: add standard version for releases'
$ yarn standard-version --release-as patch
🤝 Thanks for reading!
I hope this can be useful for anyone. If you use this library, feel free to reach out. I'd love to hear about your experiences with the tool. Also, feel free to reach out if you have great ideas to make it better or if you are stuck. 🙆♂️
📚 Extra goodies
Join the Expo Developers slack for Expo related stuff.
Join the Node Tooling slack for Standard version or Conventional Commits stuff.
Read more about Standard Version
Find out everything about the Standard Version Expo integration
Edit: I made a mistake of not adding
package.json
to our.versionrc.js
. Thanks to awinograd for spotting and fixing it!Cover photo by Robert Metz
Top comments (10)
This is an awesome write up, and great tool! Thank you.
I am planning on releasing different instances of my app per customer (each customer is a company with multiple users - each company gets its own backend "stack").
So I have different app.config.js per customer. When I do a build, upload and/or publish, I specify the --config (e.g.
expo build:ios --config customer1/app.config.js --release-channel customer1
).If I have dozens of customers & customer-configs, thoughts on how to go about keeping these all balanced? Would I simply add more entries to bumpFiles per customer, thus they all get "bumped" at the same time? (My goal is to keep all customers running essentially the same version - each just get their own splash screen/icon/app-name & API keys to the backend stack).
I guess one approach would be to use JS code in
.versionrc.js
so that it dynamically adds each of the customer configurations to thebumpFiles
rather than being required to manually add them to the config. If all my customer configs are in a standard location like./app-configs/<CUSTOMERNAME>/app.config.js
, then I could easily do this dynamically....Hopefully this helps someone besides myself. Any and all feedback welcomed on the gist page:
gist.github.com/gregfenton/b81ec01...
Just this week I started building my app and pushing it to TestFlight so I'm very happy to have come across this. Thank you, Cedric. It looks to be exactly what I need to make the process a little easier.
Excellent tool! exactly what I was looking for 💪 automating CI/CD.
given that the native part of my app will rarely update, does it makes sense updating the package.json only, as a way of keeping the bundle version?
or should I update ios/android versions too and skip uploading the apk/ipa?
Hi Pavel, thanks! The tool was initially built for managed projects without native code. For the native part, we might need to add a few "version bumpers". I'll look into this asap!
The
package.json
is used by standard-version itself as a kind of "single source of truth". So it's always good to update it there. That being said, only updating the version inpackage.json
will still require you to manually update the build numbers, likeversionCode
in Gradle, when you publish to stores.Hope it helps!
This is excellent!
hey @bycedric, how to integrate it when we're using eas server with "autoIncrement": true to manage the buildNumber and versionCode?
Actually currently these are already upgraded on eas server I just need to track those back in my repo, may be by making a commit to my repo after getting this info from eas. Something like this?
This is a great helper! Thank a lot Cedric!
No problem! Thanks for reading it! 😁