How we Automate Mobile at Shyp with CircleCI

At Shyp we have 4 mobile apps and a relatively small team. To be effective and move fast we automate everything we can: testing, building, deployments, (we even automate waiting, seriously, come to the event on Thursday to learn more about that). We have a customer facing app for Android and for iOS, an iOS app for our couriers (Compass), and an iOS app for operations team in our processing facility (Anchor).

Each of these exists in its own github repo and each repo is connected to CircleCI. In addition, we have three repos that contain various code collections shared by the iOS apps. As you can imagine, CircleCI saves us a ton of time, here’s how we do it.

With CircleCI you often have a choice to configure your project settings either through the web or in the yml file. We try to do configuration in the yml file when possible in order to review and track changes. Here is how we setup a yml file for a typical iOS app project.


      - /WIP\-.*/

    version: "7.3"

    - url:

The general - branches -ignore section tells CircleCI to skip processing any commits on branches that start with “WIP-“, which allows us to push Work In Progress branches to github without worrying about failing builds or tests.

In the machine section we explicitly name the Xcode version and set an environment variable. The SKIP_SHYP_DEV_VALIDATIONS variable concerns a mechanism we’ve kludged into our Podfile that does some validation of the development environment whenever we pod install (for example making sure our git hooks are installed in the local working directory).

On CircleCI this validation would fail, but it doesn’t matter in that context so we skip it.

Under notify - webhooks we set up a ping to our Slackerbot app. This hook looks for CI failures, figures out which developer made the commit and at what step in the CI process it failed. If it looks like a legitmate build or test failure (as opposed to an occasional CircleCI hiccup), it maps the github name to a Slack nick and posts @devnick-- for breaking CI to our slack channel. This “minus minus” deducts from their Slack score. The criteria for legitimacy is that the failure is within the build or test steps: (step_name =~ /xcodebuild/ || step_name =~ /gradlew/). We already use the Slack integration in the CircleCI

Project Settings to report build failures, but that lacked sufficient public shaming.


    - set -o pipefail &&
            -sdk iphonesimulator
            -destination 'platform=iOS Simulator,OS=8.4,name=iPhone 5'
            -workspace Shyp.xcworkspace
            -scheme "Shyp"
            clean build |
          tee $CIRCLE_ARTIFACTS/xcode_build_raw.log |
          xcpretty --color
    - set -o pipefail &&
            -sdk iphonesimulator
            -destination 'platform=iOS Simulator,OS=8.4,name=iPhone 5'
            -workspace Shyp.xcworkspace
            -scheme "Shyp"
            test |
          tee $CIRCLE_ARTIFACTS/xcode_test_raw.log |
          xcpretty --color --report junit --output $CIRCLE_TEST_REPORTS/xcode/test_results.xml

The test - override section is split into two steps. The first does a build and the second runs the tests. The CircleCI support team suggested this split to make it easier to track down intermittent build issues which were happening early on when iOS support was still experimental. We probably could combine these again but it doesn’t hurt much to leave them separated, and might be helpful if issues arise again. The output of each is teeed into a separate artifact log.

As developers we tend to build and test against the most recent iOS version throughout the day, so we felt it wise to run the automated tests against an older build destination. Our apps generally still support iOS 8.0. The oldest simulator versions CircleCI provides are 8.4 so we select that, on an iPhone 5. We pipe to xcpretty for better display on the website, and junit style test reports.


        branch: /^epic\-\-.*/
            - sh
        branch: /^feature\-\-.*/
            - sh
        branch: /^master$/
            - sh
        branch: /^release\--?\d+\.\d+\.\d+/
            - sh appstore

In the deployment section we match on certain branch names to do a deploy. Most deploys are for internal testing so they use our Enterprise certificate for code signing. Only the release--x.y.z branches use the AppStore code signing. Here is the deploy script:

# This is run by CircleCI in matched deploy sections. See circle.yml

set -e #exit immediately on any errors

if [ "$1" == 'appstore' ]; then
    SCHEME="Shyp Beta"

ls -l certificates/

if [ "$1" != 'appstore' ]; then
    echo "\n--- Decrypting certificates/${CERT_NAME}.encrypted"
    openssl aes-256-cbc -d -a -k $P12_PASSWORD -in  certificates/$CERT_NAME.encrypted -out certificates/$CERT_NAME

    ls -l certificates/

    echo "\n--- Importing certificates/${CERT_NAME}"
    security import certificates/$CERT_NAME -k circle.keychain -T /usr/bin/codesign -P ''

    echo "\n--- Listing code signing identities"
    security find-identity -p codesigning circle.keychain # List all keysiging identities

marketing_version=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" Shyp/${PLIST})

echo "\n--- Updating build version to ${build_version}"
cp Shyp/${PLIST} Shyp/${PLIST}.save
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${build_version}" Shyp/${PLIST}

if [ "$1" != 'appstore' ]; then
    NAME=`echo "${CIRCLE_BRANCH}" | sed -e 's/feature\-\-//'`
    CFBundleDisplayName="${CIRCLE_BUILD_NUM} ${NAME}"
    echo "\n--- Updating build display name to ${CFBundleDisplayName}"
    /usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName ${CFBundleDisplayName}" "Shyp/${PLIST}"

echo "\n--- Building scheme=${SCHEME} ipa=${IPA}"
gym --use_legacy_build_api --scheme "${SCHEME}" -n "${IPA}"

mv Shyp/${PLIST}.save Shyp/${PLIST}

if [ "$1" == 'appstore' ]; then
    echo "\n--- Uploading to TestFlight"
    bundle exec pilot upload
    echo "\n--- Uploading to S3"
    bundle exec ruby scripts/upload_to_s3.rb $UPLOAD_TYPE

This current deploy script illustrates both the old and new way of doing code signing on CircleCI. The new way is used for the appstore builds. In this case the code signing certificate is uploaded to CircleCI and they encrypt it on their end for storage, and decrypt it and install in a local keychain when running a build.

install certificates step screenshot

The old way involved encrypting the signing certificate p12 file yourself, including the encrypted p12 in your repo, setting the decryption password in the CircleCI Project Settings as an environment variable ($P12_PASSWORD here), and then decrypting and installing it yourself in the deploy script. The new way is nicer.

During the deploy we use the CIRCLE_BUILD_NUM as a fourth semantic versioning field appended to the marketing version as the build version number. This makes it easier for testers to attribute app behavior to particular CI builds. We also use CIRCLE_BRANCH to update the app name with build number and the distinctive part of the branch name. This typically gets truncated on the device home screen, but it is good enough to be helpful for our QA team.

The final .ipa (iOS) or .apk (Android) binaries are uploaded to our S3 storage, or TestFlight for app store release builds.

Our S3 bucket has a web frontend which allows installation directly to device.

For our Compass and Anchor buids we deploy both Beta and Live versions (these are versions with different bundle ids) at the same time. We field-test the Beta builds in one processing facility for a few days before rolling out a new live version nationwide. It’s useful to be able to have the Live and Beta versions co-exist on the field-testing devices at the same time. If a new version has a production issue the employee can always fall back to the current Live version in an instant. It’s better to simply build and store the Beta and Live versions together to make absolutely sure code changes don’t sneak in between the two.

We are pleased with the CircleCI’s iOS features and support. Making things like fastlane and xcpretty available is quite helpful. Also, they tend to keep up with the latest Xcode versions. Whenever a problem arises, the support team has been responsive. In particular the ability to ssh directly into the machine during a problem build to investigate the situation ourself has been able to speed up problem identification a few times. CircleCI is an vital component of our mobile engineering process at Shyp.

Automating Web projects with CircleCI

We rely heavily on CircleCI to test, build, and deploy our Web projects here at Shyp. We’ve got quite a few projects that are deployed by Circle, including:

  • (our main marketing site and signup/onboarding flow)
  • Various internal tools for Customer Experience and Operations teams
  • Our tracking site (“”) and referral site (“”)

All of these sites consist of static assets (JavaScript, SCSS stylesheets, EJS templates, images) that we compile and optimize as part of the build process, and some contain additional server logic for dynamic pages and routes.


Whenever a new pull request is opened against the repository, we run:

  • Static analysis linters (eslint, stylelint)
  • A complete build (producing bundled assets - SCSS, JS, EJS). This acts as a basic sanity-check and is also used when deploying a built copy (we’ll get there later on).
  • Hundreds of unit tests for frontend and server code
  • Server integration tests - we spin up a local web server and make some sample requests to validate things like routing, redirects, middleware are being applied correctly.
  • UI Automation tests using Selenium WebDriver

These tasks are all broken down into Makefile targets and shell scripts, so our circle.yml block ends up looking something like:

    - make lint
    - make stylelint
    - NODE_ENV=production make build
    - make test-client
    - make test-server
    - make test-ui

We recently set up the UI automation test suite, and have been using this both to assert that our interface is behaving correctly, and to record screenshots at various points in the UI. CircleCI containers provide a chromedriver binary, so using the node.js selenium-webdriver module is as simple as:

const webdriver = require('selenium-webdriver');
const driver = new webdriver.Builder()
  .withCapabilities({ browserName: 'chrome' })

We’ve added a small helper function so that we can take screenshots from anywhere in our test suite:

function saveScreenshot(filename) {
  const dir = process.env.CIRCLE_ARTIFACTS || '/tmp';
  const screenshotPath = `${dir}/${filename}`;

  return driver.takeScreenshot().then((image) => {
    return writeFileAsync(screenshotPath, image, 'base64');

Any files we add to the CIRCLE_ARTIFACTS directory are persisted by CircleCI in their “Arifacts” tab, so we can easily click through from a GitHub pull request to review them:

In the future, we’d like to possibly embed key screenshots as comments on each pull request, and run some automated visual regression tests.


Many of our projects use CircleCI’s Heroku integration directly, but some have more complicated needs. One example is one of our internal tools, which we deploy statically to S3 using their ‘website hosting’ feature.

Rather than setting up bucket configuration by hand and risking inconsistencies as we add deployments, we’ve committed the configuration files to the repository and use CircleCI to apply them to the buckets we deploy to:

  - make build
  - aws s3api put-bucket-website --bucket --website-configuration file://aws/aws-website-config.json --region us-west-1
  - aws s3api put-bucket-policy --bucket --policy file://aws/aws-policy-prod.json --region us-west-1
  - aws s3 sync dist s3:// --acl public-read --region us-west-1

This lets us manage the configuration with source control and add new deployment environments simply by creating more buckets.

Another example is a project that we deploy to Heroku, but which needs to compile some static assets before it can be served. We didn’t want to check the compiled code into source control, and we don’t want to run the compilation on Heroku since the build process is fairly slow and involves pulling in some private dependencies. Instead, we compile the bundle on CircleCI, create a Git commit, and push it to the Heroku deployment apps ourselves:

    - git config ""
    - git config "CircleCI VM Artifact Builder"

    branch: production
      - git add -f dist
      - git commit -m "Add built PROD artifact @ $(date)"
      - git push -f production:master

We’ve been very happy with CircleCI for all of our CI and deployment needs!

Shyp is the shipping made easy startup whose goal is to help customers never go to the post office again. Join us for April Office Hours tomorrow and learn more about Shyp’s CLI from their team of developers.