How we Automate Mobile and Web Projects at Shyp with CircleCI

Dan Rummel
Shyp Engineering
Published in
7 min readApr 19, 2016

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.

Setup

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.

Testing

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.

Deploying

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 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.

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 a 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:

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

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.

Testing

Whenever a new pull request is opened against the Shyp.com 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:

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' })
.build();

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 “Artifacts” 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.

Deployment

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:

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:

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

--

--

Dan Rummel
Shyp Engineering

Technology Mentor, Advisor, Eng. Leader, (Now @ One Medical, Previously @ Shyp, Live Nation, …) :: Believer in the power of people