My team and I are working on automating how we release our Expo app. We use the managed workflow — with development builds since we have config plugins — and expo-updates
for OTA updates.
Here’s the goal:
-
When a PR is merged with no binary changes, update the OTA preview channel:
eas update --branch preview --message "$(git rev-parse --abbrev-ref HEAD)"
-
When a PR is merged with binary changes, kick off new preview and production channel builds:
eas build --profile preview eas build --profile production
The question is: how do I detect whether or not the PR necessitates a new binary?
I’ve come up with a few ways, none of which I love:
-
Naive approach: if there are any changes to
app.config.js
orpackage.json
, trigger a new binary build.→ This is not great, since not every change to
package.json
means that a new package has been added. For example, you could change somethingscripts
or add/remove/update a package that has no native code, just JS. So, this will lead to a lot of unnecessary builds. -
Use
expo prebuild
: On both the source and target branch of the PR, run prebuild and compute a checksum of theios
andandroid
dirs, then compare the two.(see example code)
STAGING_DIR=$TMDDIR/source_checksum # Source branch git checkout $GITHUB_HEAD_REF # (GitHub Actions env var) rm -rf node_modules ios android $STAGING_DIR yarn && expo prebuild mkdir $STAGING_DIR && mv ios $STAGING_DIR && mv android $STAGING_DIR find -s $STAGING_DIR -type f -exec md5sum {} \; | md5sum > $TMPDIR/source_branch_checksum # Target branch git checkout $GITHUB_BASE_REF # (GitHub Actions env var) rm -rf node_modules ios android $STAGING_DIR yarn && expo prebuild mkdir $STAGING_DIR && mv ios $STAGING_DIR && mv android $STAGING_DIR find -s $STAGING_DIR -type f -exec md5sum {} \; | md5sum > $TMPDIR/target_branch_checksum # If the two checksums are not equal, there are binary changes diff $TMPDIR/source_branch_checksum $TMPDIR/target_branch_checksum || echo "::set-output name=binary_changes::true" # (GitHub Actions syntax for step output)
→ Unfortunately I think this only catches changes to packages that have config plugins. If a package is added that has native code but no config plugin, it may not cause any change to the files produced by
expo prebuild
. -
Use
expo config
: Maybe there’s a way to leverageexpo config --type introspect
or something similar. I think this has the same issue as (2) though, that it only detects changes to packages with config plugins. -
Parse dependencies: parse the dependencies in
node_modules
for both the source and target branch, filtered to just those that containios/
orandroid/
directories. If the versions of any of these packages have changed (or if any packages have been added/removed), trigger a new binary build. (Or, if there are any changes toapp.config.js
, trigger a new binary build.)See code: Gist
→ This feels like the best approach so far, but if a non-native package containing an
ios/
orandroid/
directory somewhere was modified, we’d get a false positive.