<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>CircleCI &#8211; Zhijun Chen</title>
	<atom:link href="https://zhijunchen.com/tag/circleci/feed/" rel="self" type="application/rss+xml" />
	<link>https://zhijunchen.com</link>
	<description></description>
	<lastBuildDate>Fri, 07 Jun 2024 06:29:46 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.5.4</generator>

<image>
	<url>https://zhijunchen.com/wp-content/uploads/2021/03/cropped-my-site-icon-1-32x32.png</url>
	<title>CircleCI &#8211; Zhijun Chen</title>
	<link>https://zhijunchen.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Set Up Detox For Expo Project Using CircleCI</title>
		<link>https://zhijunchen.com/set-up-detox-for-expo-project-using-circleci/</link>
		
		<dc:creator><![CDATA[Zhijun Chen]]></dc:creator>
		<pubDate>Fri, 26 May 2023 09:05:23 +0000</pubDate>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[CircleCI]]></category>
		<category><![CDATA[Detox]]></category>
		<category><![CDATA[Expo]]></category>
		<category><![CDATA[React Native]]></category>
		<guid isPermaLink="false">https://zhijunchen.com/?p=201</guid>

					<description><![CDATA[Recently our Frontend team started to think about automated testing for our Expo managed project. Following discussion we decided to use Detox as our E2E testing tool (We evaluated Appium and Detox). Since we already have a CircleCI pipeline running based on Expo EAS local build and automatic submission, it makes sense to integrate Detox [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Recently our Frontend team started to think about automated testing for our <a href="https://expo.dev/" target="_blank" rel="noreferrer noopener">Expo</a> managed project. Following discussion we decided to use <a href="https://wix.github.io/Detox/" target="_blank" rel="noreferrer noopener">Detox</a> as our E2E testing tool (We evaluated <a href="https://appium.io/docs/en/2.0/" target="_blank" rel="noreferrer noopener">Appium</a> and Detox). Since we already have a <a href="https://circleci.com/" data-type="URL" data-id="https://circleci.com/" target="_blank" rel="noreferrer noopener">CircleCI</a> pipeline running based on <a href="https://expo.dev/eas" target="_blank" rel="noreferrer noopener">Expo EAS</a> local build and automatic submission, it makes sense to integrate Detox into our pipeline. In additional we also want to enable easy local development when writing E2E tests. After research we have reached the following conclusions/setup:</p>



<ul>
<li>Local development: We will use Expo Go app together with Detox so that we can refresh the app and update our tests easily.</li>



<li>CI pipeline: We will build standalone artefacts and use CircleCI MacOS and Android executors to run the same test suites</li>
</ul>



<p>At the time of writing the team is using Expo 46 and Detox 19.</p>



<h2 class="wp-block-heading">Set Up Detox</h2>



<p>On your project, run the following command to install required dependencies</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">npm install --save-dev @config-plugins/detox detox@19.9.0 detox-expo-helpers jest</code></pre>



<p><code><a href="https://github.com/expo/config-plugins" target="_blank" rel="noreferrer noopener">@config-plugins/detox</a></code> is used to help generate detox compatible artifacts when expo runs the build.</p>



<p><code><a href="https://github.com/expo/detox-expo-helpers" target="_blank" rel="noreferrer noopener">detox-expo-helpers</a></code> is needed when we use Expo Go locally to communicate with detox, more on this later.</p>



<p><code><a href="https://jestjs.io/" target="_blank" rel="noreferrer noopener">jest</a></code> is required because detox does not have its own test-runner.</p>



<p>Now, open&nbsp;<code>app.json</code>&nbsp;and add the&nbsp;<code>@config-plugins/detox</code>&nbsp;plugin to your&nbsp;<code>plugins</code>&nbsp;list (this must be done before prebuilding). This will automatically configure the Android native code to support Detox.</p>



<pre class="wp-block-code"><code lang="json" class="language-json">{
  "expo": {
    // ...
    "plugins": ["@config-plugins/detox"]
  }
}</code></pre>



<p>Create Detox configuration files using the following command:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">npx detox init -r jest</code></pre>



<p>Add <code>.detoxrc.json</code> to project root dir. Note that <code>ios-expo</code> is to support local development</p>



<pre class="wp-block-code"><code lang="json" class="language-json">{
    "testRunner": "jest",
    "runnerConfig": "e2e/config.json",
    "skipLegacyWorkersInjection": true,
    "apps": {
        "ios": {
            "type": "ios.app",
            "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/MyApp.app",
            "build": "eas build --platform ios --profile detox-test --local --clear-cache"
        },
        "android": {
            "type": "android.apk",
            "binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
            "testBinaryPath": "android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk",
            "build": "eas build --platform android --profile detox-test --local --clear-cache"
        },
        // Local configuration
        "ios-expo": {
            "type": "ios.app",
            "binaryPath": "bin/Exponent.app"
        }
    },
    "devices": {
        "simulator": {
            "type": "ios.simulator",
            "device": {
                "type": "iPhone 14"
            }
        },
        "emulator": {
            "type": "android.emulator",
            "device": {
                "avdName": "pixel_4"
            }
        }
    },
    "configurations": {
        "ios": {
            "device": "simulator",
            "app": "ios"
        },
        "android": {
            "device": "emulator",
            "app": "android"
        },
        // Local configuration
        "ios-expo": {
            "device": "simulator",
            "app": "ios-expo"
        }
    }
}</code></pre>



<p>Update <code>eas.json</code> to include detox test details</p>



<pre class="wp-block-code"><code lang="json" class="language-json">{
    // ...
    "build": {
        "detox-test": {
            "distribution": "internal",
            "ios": {
                "simulator": true
            },
            "android": {
                "gradleCommand": ":app:assembleRelease :app:assembleAndroidTest -DtestBuildType=release",
                "withoutCredentials": true
            }
        },
        // ...
    },
    // ...
}
</code></pre>



<h2 class="wp-block-heading">Local Development</h2>



<p>For local development we will use <code>Expo</code> app instead of building standalone apps because it will be faster to reflect changes and run tests by following TDD practice.</p>



<p>So in <code>package.json</code> we could add a helper command like following inside <code>scripts</code> section:</p>



<pre class="wp-block-code"><code lang="json" class="language-json">{
  // ...
  scripts: {
    // ...
    "test:detox": "DETOX_ENV=expo detox test -c ios-expo",
  }
}</code></pre>



<p>And we also added a scripting file to help running the test easier</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">#!/bin/sh

EXPO_APP_DIR=bin/Exponent.app

if [ ! -d $EXPO_APP_DIR ]; then
    mkdir -p $EXPO_APP_DIR
    curl https://dpq5q02fu5f55.cloudfront.net/Exponent-2.28.7.tar.gz -o bin/expo-app.tar.gz
    tar xvzf bin/expo-app.tar.gz -C $EXPO_APP_DIR
fi

npm run test:detox</code></pre>



<p>Because <code>Expo</code> app and standalone apps open and execute the tests in a different way, at the moment we choose to accommodate our tests by using an environment variable (We use <code>expo</code> for local development and <code>standalone</code> for running in CircleCI). Hopefully this will be improved in future. An example test would look like the following:</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">const { reloadApp } = require("detox-expo-helpers");

const detoxEnv = process.env.DETOX_ENV;

describe("Example", () =&gt; {
    beforeAll(async () =&gt; {
        if (detoxEnv === "standalone") {
            await device.launchApp();
        }
    });

    beforeEach(async () =&gt; {
        if (detoxEnv === "standalone") {
            await device.reloadReactNative();
        } else if (detoxEnv === "expo") {
            await reloadApp();
            await waitFor(element(by.id("loginMessage")))
                .toBeVisible()
                .withTimeout(20000);
        }
    });

    // test cases
    // ...
});</code></pre>



<h2 class="wp-block-heading">CircleCI pipeline</h2>



<p>In CircleCI we will build the standalone apps first, start up the simulators then execute detox tests. By the time of writing, Android emulator is very unstable even with hardware acceleration on (see problems below), so we opt to run detox tests only on iOS simulator which is quite stable.</p>



<p>iOS step:</p>



<pre class="wp-block-code"><code lang="yaml" class="language-yaml">detox-test-ios:
    executor: ios
    steps:
      - attach_workspace:
          at: ~/
      - node/install:
          install-yarn: true
          node-version: '16.9.1'
      - run: echo -n $ARTIFACTORY_NPMRC | base64 -d  &gt; ~/.npmrc
      - restore_cache:
          name: Restoring npm cache
          keys:
            - npm-ios-detox-{{.Environment.CIRCLE_PROJECT_REPONAME}}-{{ checksum "package-lock.json" }} # Primary cache
            - npm-ios-detox-{{.Environment.CIRCLE_PROJECT_REPONAME}} # Fallback cache
      - run:
          name: Install Apple sim utils
          command: |
            brew tap wix/brew
            brew install applesimutils
      - run:
          name: ios build
          command: |
            npm install -g expo-cli eas-cli detox-cli
            npm ci
            detox build -c ios
            mkdir -p ios/build/Build/Products/Release-iphonesimulator/
            mv *.tar.gz ios/build/Build/Products/Release-iphonesimulator/app-build.tar.gz
            cd ios/build/Build/Products/Release-iphonesimulator/ &amp;&amp; tar xvzf app-build.tar.gz &amp;&amp; cd -
      - run:
          name: ios test
          command: DETOX_ENV=standalone detox test -c ios --headless --take-screenshots failing --record-videos failing
      - store_artifacts:
          path: artifacts/
      - save_cache:
          name: Saving npm cache
          paths:
            - ~/.npm
          key: npm-ios-detox-{{.Environment.CIRCLE_PROJECT_REPONAME}}-{{ checksum "package-lock.json" }}</code></pre>



<p>Android step:</p>



<pre class="wp-block-code"><code lang="yaml" class="language-yaml">detox-test-android:
    machine:
      image: android:2023.02.1
    resource_class: xlarge
    steps:
      - attach_workspace:
          at: ~/
      - node/install:
          install-yarn: true
          node-version: '16.9.1'
      - run: echo -n $ARTIFACTORY_NPMRC | base64 -d  &gt; ~/.npmrc
      - restore_cache:
          name: Restoring npm cache
          keys:
            - npm-android-detox-{{.Environment.CIRCLE_PROJECT_REPONAME}}-{{ checksum "package-lock.json" }} # Primary cache
            - npm-android-detox-{{.Environment.CIRCLE_PROJECT_REPONAME}} # Fallback cache
      - run: echo -n $PLAY_STORE_SERVICE_ACCOUNT_KEY | base64 -d  &gt; serviceAccountKey.json            
      - run:
          name: Android build and test
          command: |
            sudo apt-get --quiet update --yes
            sudo apt-get --quiet install --yes \
              libc6 \
              libdbus-1-3 \
              libfontconfig1 \
              libgcc1 \
              libpulse0 \
              libtinfo5 \
              libx11-6 \
              libxcb1 \
              libxdamage1 \
              libnss3 \
              libxcomposite1 \
              libxcursor1 \
              libxi6 \
              libxext6 \
              libxfixes3 \
              zlib1g \
              libgl1 \
              pulseaudio \
              socat

            yes | sdkmanager --licenses || if [[ $? -eq 141 ]]; then true; else exit $?; fi
            sdkmanager --install "system-images;android-32;google_apis;x86_64"
            avdmanager --verbose create avd --force --name "pixel_4" --device "pixel_4" --package "system-images;android-32;google_apis;x86_64"            

            $ANDROID_SDK_ROOT/emulator/emulator @pixel_4 -no-audio -no-boot-anim -no-window -use-system-libs -wipe-data -accel on -gpu host 2&gt;&amp;1 &gt;/dev/null &amp;

            max_retry=10
            counter=0
            until adb shell getprop sys.boot_completed; do
              sleep 10
              [[ counter -eq $max_retry ]] &amp;&amp; echo "Failed to start the emulator!" &amp;&amp; exit 1
              counter=$((counter + 1))
            done

            npm install -g expo-cli eas-cli detox-cli
            npm ci
            detox build -c android
            mkdir -p android/app/build/outputs/apk/
            mv *.tar.gz android/app/build/outputs/apk/app-build.tar.gz
            cd android/app/build/outputs/apk/ &amp;&amp; tar xvzf app-build.tar.gz &amp;&amp; cd -
            adb shell input keyevent 82
            DETOX_ENV=standalone detox test -c android --headless --take-screenshots failing --record-videos failing
      - run:
          name: clean up
          when: always
          command: adb emu kill &amp;
      - store_artifacts:
          path: artifacts/
      - save_cache:
          name: Saving npm cache
          paths:
            - ~/.npm
          key: npm-android-detox-{{.Environment.CIRCLE_PROJECT_REPONAME}}-{{ checksum "package-lock.json" }}</code></pre>



<h2 class="wp-block-heading">Problems Occurred During Set Up</h2>



<ul>
<li><strong>Android build library conflicts</strong></li>
</ul>



<p>When building Android under the versions we use, we came across duplicates/conflicts for some of the libraries. In order to fix this, we used a custom expo plugin to automatically pick the first library during <code>eas build</code> process. So under <code>plugins</code> folder, create a file named <code>withAndroidPickFirst.js</code> with the following content:</p>



<pre class="wp-block-code"><code lang="javascript" class="language-javascript">const { withAppBuildGradle } = require("@expo/config-plugins");

function addPickFirst(buildGradle, paths) {
    const regexpPackagingOptions = /\bpackagingOptions\s*{/;
    const packagingOptionsMatch = buildGradle.match(regexpPackagingOptions);

    const bodyLines = [];
    paths.forEach((path) =&gt; {
        bodyLines.push(`        pickFirst '${path}'`);
    });
    const body = bodyLines.join("\n");

    if (packagingOptionsMatch) {
        console.warn(
            "WARN: withAndroidPickFirst: Replacing packagingOptions in app build.gradle"
        );
        return buildGradle.replace(
            regexpPackagingOptions,
            `packagingOptions {
                ${body}`
        );
    }

    const regexpAndroid = /\bandroid\s*{/;
    const androidMatch = buildGradle.match(regexpAndroid);

    if (androidMatch) {
        return buildGradle.replace(
            regexpAndroid,
            `android {
                packagingOptions {
                    ${body}
                }`
        );
    }

    throw new Error(
        "withAndroidPickFirst: Could not find where to add packagingOptions"
    );
}

module.exports = (config, props = {}) =&gt; {
    if (!props.paths) {
        throw new Error("withAndroidPickFirst: No paths specified!");
    }
    return withAppBuildGradle(config, (config) =&gt; {
        if (config.modResults.language === "groovy") {
            config.modResults.contents = addPickFirst(
                config.modResults.contents,
                props.paths
            );
        } else {
            throw new Error(
                "withAndroidPickFirst: Can't add pickFirst(s) because app build.gradle is not groovy"
            );
        }
        return config;
    });
};</code></pre>



<p>Then inside <code>app.json</code> file, add the following to <code>plugins</code> section:</p>



<pre class="wp-block-code"><code lang="json" class="language-json">"plugins": [
   // ...
   [
     "./plugins/withAndroidPickFirst",
     {
       "paths": [
          "lib/**/libc++_shared.so",
          "lib/**/libreactnativejni.so",
          "lib/**/libreact_nativemodule_core.so",
          "lib/**/libglog.so",
          "lib/**/libjscexecutor.so",
          "lib/**/libfbjni.so",
          "lib/**/libfolly_json.so",
          "lib/**/libfolly_runtime.so",
          "lib/**/libhermes.so",
          "lib/**/libjsi.so"
       ]
      }
   ]
 ],</code></pre>



<ul>
<li><strong>Android emulator performance issues</strong></li>
</ul>



<p>Initially we aimed to use both iOS and Android for CircleCI pipeline but later on found out that Android emulator is quite unstable due to the error <code>System UI isn't responding</code> popup. This causes the tests to fail because we would need to click <code>Dismiss</code> from the popup. Even though we tried to add hardware acceleration to the emulator, it still doesn&#8217;t fix the issue completely.</p>



<ul>
<li><strong>detox-expo-helpers local communication issues</strong></li>
</ul>



<p>The version we are using has an issue where it could not properly communicate with Expo app to execute the tests. After some investigation it seems that we have to manually patch the library to get around. Fortunately there is <code><a rel="noreferrer noopener" href="https://github.com/ds300/patch-package" data-type="URL" data-id="https://github.com/ds300/patch-package" target="_blank">patch-package</a></code> which allows us to apply a diff to the library. Install patch-package using npm:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">npm install --save-dev patch-package</code></pre>



<p>Then create the following diff  <code>detox-expo-helpers+0.6.0.patch</code> under <code>patches</code> folder (you can also use patch-package to generate this for you): </p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">diff --git a/node_modules/detox-expo-helpers/index.js b/node_modules/detox-expo-helpers/index.js
index 864493b..e9165dd 100644
--- a/node_modules/detox-expo-helpers/index.js
+++ b/node_modules/detox-expo-helpers/index.js
@@ -70,7 +70,13 @@ const reloadApp = async (params) =&gt; {
     newInstance: true,
     url,
     sourceApp: 'host.exp.exponent',
-    launchArgs: { EXKernelDisableNuxDefaultsKey: true, detoxURLBlacklistRegex: formattedBlacklistArg },
+    launchArgs: {
+      EXKernelDisableNuxDefaultsKey: true,
+      detoxURLBlacklistRegex: formattedBlacklistArg,
+      detoxEnableSynchronization: 0,
+      ...(params &amp;&amp; params.launchArgs),
+    },
+    ...(params &amp;&amp; params.args),
   });</code></pre>



<p>Finally execute the patch after npm post install by specifying this in <code>package.json</code> file:</p>



<pre class="wp-block-code"><code lang="json" class="language-json">{
  // ...
  scripts: {
    // ...
    "postinstall": "patch-package",
  }
}</code></pre>



<h2 class="wp-block-heading">References</h2>



<ul>
<li><a rel="noreferrer noopener" href="https://docs.expo.dev/build-reference/e2e-tests/" target="_blank">Running E2E tests on EAS Build</a></li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using CircleCI, Expo EAS, BrowserStack to build React Native Application Deployment Pipeline</title>
		<link>https://zhijunchen.com/using-circleci-expo-eas-browserstack-to-build-react-native-application-deployment-pipeline/</link>
		
		<dc:creator><![CDATA[Zhijun Chen]]></dc:creator>
		<pubDate>Tue, 16 Aug 2022 08:32:47 +0000</pubDate>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[BrowserStack]]></category>
		<category><![CDATA[CircleCI]]></category>
		<category><![CDATA[Expo]]></category>
		<category><![CDATA[React Native]]></category>
		<guid isPermaLink="false">https://zhijunchen.com/?p=186</guid>

					<description><![CDATA[I recently got a chance to work within a Frontend team which delivers iOS and Android applications to our customers. One of the pain point in the team was the difficulty in app testing and release so I spent some time on investigating the best approach for the team. The team is using React Native [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p style="font-size:18px">I recently got a chance to work within a Frontend team which delivers iOS and Android applications to our customers. One of the pain point in the team was the difficulty in app testing and release so I spent some time on investigating the best approach for the team. The team is using <a rel="noreferrer noopener" href="https://reactnative.dev/" target="_blank">React Native</a> based <a rel="noreferrer noopener" href="https://expo.dev/" target="_blank">Expo</a> and at this stage highly replying on manual testing on physical devices. They are currently putting efforts on automated testing based on <a rel="noreferrer noopener" href="https://wix.github.io/Detox/" target="_blank">Detox</a>.</p>



<h2 class="wp-block-heading">Expo EAS</h2>



<p style="font-size:18px">Expo provides a React Native build tool called <a rel="noreferrer noopener" href="https://expo.dev/eas" target="_blank">Expo Application Services (EAS)</a>, it allows you to build and submit applications easily. By default when you run <code>eas build</code>, it will create a build on their cloud services and you will have to pay for priority pipeline execution. The Free plan at the time of writing only allows you to build one pipeline at a time. In our case we already have <a rel="noreferrer noopener" href="https://circleci.com/" target="_blank">CircleCI</a> subscription so it would be better if we can build app bundles there. Fortunately there is a <code>--local</code> option which allows building app bundles locally. You will still need to set up EAS on Expo, for detail guide, please refer to their <a rel="noreferrer noopener" href="https://docs.expo.dev/build/setup/" target="_blank">setup guide</a>. Here is an example command we use to build our app bundles using CircleCI MacOS and JVM executor.</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">eas build --profile preview --platform ios --clear-cache --local --non-interactive --output "my-app.ipa"</code></pre>



<p style="font-size:18px">We use <code>eas submit</code> to automatically push master/production build to app store and play store after manual approval. The key here is that for BrowserStack we need to use internal distribution profile so that we can install on their devices. You don&#8217;t have to add their remote devices to Expo as BrowserStack re-signs the bundles upon upload so that they can be installed on their devices.</p>



<h2 class="wp-block-heading">BrowserStack</h2>



<p style="font-size:18px">To make current manual testing easier I decided to introduce <a rel="noreferrer noopener" href="https://www.browserstack.com/docs/app-live" target="_blank">BrowserStack App Live</a>, it allows the team to test the app using remote real devices and it offers quite a lot of iOS and Android options. It also comes with <a rel="noreferrer noopener" href="https://www.browserstack.com/docs/local-testing" target="_blank">local testing</a> which allows us to connect to our backend services hosted behind our vpn. There are some options for getting access to the app. You can upload app <code>.ipa</code> and <code>.apk</code> bundles, or install via <a href="https://apps.apple.com/us/app/testflight/id899247664">Apple TestFlight</a> and <a rel="noreferrer noopener" href="https://play.google.com/store/" target="_blank">Google Play</a>. To test push notification, please refer to guide <a rel="noreferrer noopener" href="https://www.browserstack.com/question/40307" target="_blank">here</a>. If you are uploading the <code>.ipa</code> Apple bundle, unfortunately it is not supported at the time of writing, the only way to test push notification on iOS is installation via TestFlight.</p>



<h2 class="wp-block-heading">Overall Pipeline View</h2>



<p style="font-size:18px">Here is what our overall pipeline looks like:</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="441" src="https://zhijunchen.com/wp-content/uploads/2022/08/tutor-app-pipeline-1024x441.png" alt="" class="wp-image-193" srcset="https://zhijunchen.com/wp-content/uploads/2022/08/tutor-app-pipeline-1024x441.png 1024w, https://zhijunchen.com/wp-content/uploads/2022/08/tutor-app-pipeline-300x129.png 300w, https://zhijunchen.com/wp-content/uploads/2022/08/tutor-app-pipeline-768x331.png 768w, https://zhijunchen.com/wp-content/uploads/2022/08/tutor-app-pipeline.png 1372w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p style="font-size:18px">For branch build we have a manual trigger to push bundles to BrowserStack as we don&#8217;t want every commit to go through manual QA process. We only trigger this once the PR has been reviewed and ready for QA. The bundles are also uploaded to S3 so that we can retrieve them and install locally easily. You will need to register your test physical devices with Expo using <code>eas device:create</code> so that the bundles can be installed. See internal distribution setup <a href="https://docs.expo.dev/build/internal-distribution/" target="_blank" rel="noreferrer noopener">here</a>.</p>



<p style="font-size:18px">For master/main branch build we went through the similar process with the only difference that builds are automatically pushed to BrowserStack with a manual trigger to push to production following production profile build. We use <a rel="noreferrer noopener" href="https://github.com/conventional-changelog/standard-version" target="_blank">standard-version</a> to automatically increase our app version so that every build is able to be released. The BrowserStack preview here is quite useful during our review session and following that we will decide whether the candidate version is good enough for release.</p>



<h2 class="wp-block-heading">CircleCI config</h2>



<p style="font-size:18px">Here are some useful CircleCI steps for the pipeline described above:</p>



<h5 class="wp-block-heading">Build Android Using OpenJDK executor</h5>



<pre class="wp-block-code"><code lang="yaml" class="language-yaml">eas-build-android:
    executor: android
    parameters:
      profile: 
        description: distribution profile
        type: string
        default: preview
    environment:
      PROFILE: &lt;&lt; parameters.profile &gt;&gt;
    steps:
      - attach_workspace:
          at: ~/
      - node/install:
          install-yarn: true
          node-version: '16.9.1'
      - run: echo -n $PLAY_STORE_SERVICE_ACCOUNT_KEY | base64 -d  &gt; serviceAccountKey.json        
      - run:
          name: android build
          command: |
            mkdir android_sdk
            curl -O https://dl.google.com/android/repository/commandlinetools-linux-8512546_latest.zip
            unzip commandlinetools-linux-8512546_latest.zip
            mv cmdline-tools android_sdk
            mkdir -p android_sdk/cmdline-tools/tools
            mv -t android_sdk/cmdline-tools/tools android_sdk/cmdline-tools/lib android_sdk/cmdline-tools/bin android_sdk/cmdline-tools/source.properties android_sdk/cmdline-tools/NOTICE.txt
            export ANDROID_SDK_ROOT=$HOME/project/android_sdk
            yes | $ANDROID_SDK_ROOT/cmdline-tools/tools/bin/sdkmanager --licenses || if [[ $? -eq 141 ]]; then true; else exit $?; fi
            npm install -g expo-cli eas-cli
            npm ci
            eas build --profile &lt;&lt; parameters.profile &gt;&gt; --platform android --clear-cache --local --non-interactive --output "${CIRCLE_BRANCH//\//-}-${PROFILE}-${CIRCLE_SHA1:0:7}.apk"</code></pre>



<h5 class="wp-block-heading">Build iOS using MacOS executor</h5>



<pre class="wp-block-code"><code lang="yaml" class="language-yaml">  eas-build-ios:
    executor: ios
    parameters:
      profile: 
        description: eas profile
        type: string
        default: preview
    environment:
      PROFILE: &lt;&lt; parameters.profile &gt;&gt;
    steps:
      - attach_workspace:
          at: ~/
      - node/install:
          install-yarn: true
          node-version: '16.9.1'
      - run:
          name: ios build
          command: |
            npm install -g expo-cli eas-cli
            npm ci
            eas build --profile &lt;&lt; parameters.profile &gt;&gt; --platform ios --clear-cache --local --non-interactive --output "${CIRCLE_BRANCH//\//-}-${PROFILE}-${CIRCLE_SHA1:0:7}.ipa"</code></pre>



<p style="font-size:18px">We are using <a href="https://circleci.com/developer/orbs/orb/circleci/node">CircleCI NodeJS Orbs</a> to install NodeJS on the executors. For Android we pass in service account key from pipeline secret and need to download <a rel="noreferrer noopener" href="https://developer.android.com/studio/command-line" target="_blank">Command Line Tools</a> too.</p>



<p style="font-size:18px">Overall the pipeline works quite well for us and we have significant improvements on development process. Now product manager/designer and manual QA could easily download the bundle and play with the app on either BrowserStack or our test physical devices.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
