This post, written by CircleCI Developer Advocate Ron Powell, was originally published on The New Stack here.

Modern applications rely heavily on HTTP requests to external services to GET, POST, PUT, and DELETE data. This information transfer is done with RESTful or REST APIs to reduce the bandwidth associated with traditional APIs. REST APIs are essential for distributed computing and cloud-based technologies. One example is publishing packages to an AWS S3 bucket using the Amazon S3 REST API. Another is using cURL to download a package from the web to be installed. With these examples, it becomes clear that one would need to integrate these API calls into their CI/CD pipeline for fully automated building and deployment. In this tutorial, we will build an application that will use both of these APIs. It is a simple Scala application that we will build and deploy to an S3 bucket. The goal of this post is to show real world examples of REST API usage. Extensive knowledge of Scala is not required.

Building the app

First, let’s create a directory and cd into it by typing this command in your terminal:

mkdir scala-demo && cd $_

This is where we will add the files for our application. Now, we need to add a HelloWorld.scala file in a /src/main/ folder. Add the file by typing these two commands:

mkdir -p src/main/ && cd $_ 
touch HelloWorld.scala

Open your favorite editor and save these lines to HelloWorld.scala:

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello, World! I'm the Sample Scala App!")
  }
}

We need a few more files before we have a working application. Let’s define a few files for using sbt. The first is build.sbt and it needs to be in the projects root. Type these lines into your terminal:

cd ../.. && touch build.sbt

Return to your editor and save these lines to the file:

name := "scala-demo"

version := "0.1"

scalaVersion := "2.12.5"

lazy val root = (project in file(".")).enablePlugins(JavaAppPackaging)

//rename zip file created from dist command
packageName in Universal := "scala-demo"

Next, let’s add a couple of files to a project folder that will define variables for sbt. For the first, type this into your terminal:

mkdir project && cd $_
touch build.properties

Return to your editor and save this line to the file:

sbt.version = 1.1.4

The last file will be added with this line:

touch plugins.sbt

Return to your editor and save this line to the file:

addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.4")

Pushing to GitHub To commit our code to Git, we need to return to our project’s root and type the following lines in our terminal:

git init
git add .
git commit -m “Initial commit”

Open GitHub in your browser and create a new repository by clicking New in the top left corner of the browser. If you don’t have a GitHub account, you can create one here.


Add the name of the repo and click Create repository.


The next page provides instructions for getting started. We are going to use the third method. Type the lines in your terminal. In my case, the lines are as follows:

git remote add origin https://github.com/ronpowelljr/scala-demo.git
git push -u origin master

Using REST APIs in your pipeline

You may have used REST APIs for building an app locally, for manual scripting and/or for debugging. Tools like Wget and cURL are common tools in an engineer’s belt, so let’s explore the benefit you get from using these tools in your CI/CD pipeline. Next we’re going to use CircleCI to build our application package and deploy it to an AWS S3 bucket. If you don’t have a CircleCI account, you can sign up for one here.

Log in to your account and select ADD PROJECTS from the left nav bar. Then click Set Up Project to the right of our new project.


If we click Start building now, our project will not run. We don’t have a configuration file yet.

Return to your terminal and in your project’s root type these lines:

mkdir .circleci && cd $_
touch config.yml

Return to your editor and save these lines to the file:

version: 2
jobs:
  build:
    working_directory: ~/scala-demo
    docker:
      - image: openjdk:8
    environment:
      - SBT_VERSION: 1.0.4
    steps:
      - run: echo 'export ARTIFACT_BUILD=$CIRCLE_PROJECT_REPONAME-$CIRCLE_BUILD_NUM.zip' >> $BASH_ENV
      - run:
          name: Get sbt binary
          command: |
                    apt update && apt install -y curl
                    curl -L -o sbt-$SBT_VERSION.deb https://dl.bintray.com/sbt/debian/sbt-$SBT_VERSION.deb
                    dpkg -i sbt-$SBT_VERSION.deb
                    rm sbt-$SBT_VERSION.deb
                    apt-get update
                    apt-get install -y sbt python-pip git
                    pip install awscli
                    apt-get clean && apt-get autoclean
      - checkout
      - restore_cache:
          key: sbt-cache
      - run:
          name: Compile scala-demo dist package
          command: cat /dev/null | sbt clean update dist
      - store_artifacts:
          path: target/universal/scala-demo.zip
          destination: scala-demo
      - save_cache:
          key: sbt-cache
          paths:
            - "~/.ivy2/cache"
            - "~/.sbt"
            - "~/.m2"
      - deploy:
          command: |
              mv target/universal/scala-demo.zip $CIRCLE_ARTIFACTS/$ARTIFACT_BUILD
              aws s3 cp $CIRCLE_ARTIFACTS/$ARTIFACT_BUILD s3://ronpowelljr-scala-demo/builds/scala-demo/ --metadata {\"git_sha1\":\"$CIRCLE_SHA1\"}

Note: you will need to replace my S3 bucket location with yours.

The Get sbt binary step of the build job has a common usage of cURL: grabbing a package from the web to be installed. The deploy step shows the S3 API call to upload the artifact. Let’s commit this code to our repo so that we can start building with CIrcleCI. Return to your terminal and in your project’s root type these lines:

git add .
git commit -m “added circleci”
git push origin

Go back to CircleCI and click Start building. The first run will fail because we haven’t added our AWS credentials as environment variables. You can add your AWS creds following the instructions found here. If you don’t have an AWS account, sign up for one here. You will then need to add an S3 bucket for uploading your application package. Instructions for that can be found here. Create a default bucket with public access.

After you have them set, let’s add a README.md file to our project to kick off a new build. We’ll add a title, a description of the app, and a status badge. Return to your terminal and in your project’s root type this line:

touch README.md

Return to your editor and save these lines to the file:

# Demo Scala app [![CircleCI](https://circleci.com/gh/ronpowelljr/scala-demo.svg?style=svg)](https://circleci.com/gh/ronpowelljr/scala-demo)

This project is a Helloworld application I like to use for demos, blogs, and examples. It's a great starting point for Scala projects.

Note: you will need to replace the URL for my circleci project with yours.

After a couple of minutes, your build will pass and your package will be uploaded to your S3 bucket. Go to your S3 dashboard to make sure that the package arrived.


Early exit

Now let’s explore a novel way to include cURL in your pipeline to communicate with the Bintray REST API. It is common for a build to fail because of an external service that is down. One great way to use REST APIs in your pipeline is to check that these services are operational before continuing the build. This provides you with an opportunity to craft a custom message for this particular method of failure and eliminates any need for a developer to spend time debugging the fail.

Let’s use cURL in another job before build that will check whether or not Bintray is available to provide you with the package to download. Return to your editor and replace the lines above the build job in the config file with these lines:

workflows:
  version: 2
  check-and-build:
    jobs:
      - check
      - build:
          requires:
            - check

version: 2.1
jobs:
  check:
    docker:
      - image: circleci/python:2.7.14
    steps:
      - run:
          name: Get Bintray API status
          command: |
                    BT_STATUS=$(curl -s https://status.bintray.com/api/v2/status.json |  jq --raw-output '.status.description')
                    echo “Bintray API Status: $BT_STATUS”
                    if [ “$BT_STATUS” != “All Systems Operational” ]; then
                      echo “Error [Bintray API Status: $BT_STATUS]”
                      exit 1
                    fi 
  build:
...

We set up the Get Bintray API status step of the check job to run first and the build job is now dependent on it. It checks the status and then uses logic to fail and exit our job if the status is anything other than “All Systems Operational.” This can limit your costs as well as save time for your engineers because if Bintray is unavailable, the job doesn’t sit waiting for cURL to timeout and your engineers don’t need to spend much time investigating the fail.

Finally, commit the changes:

git add .
git commit -m “check”
git push origin

Wrapping up

REST APIs will only grow in popularity and usage. The services you need most will have REST APIs for communicating with them over the web. Start thinking of the different ways to put them to use in your pipelines. There may be a few services that you want to check the status of and the above example can be expanded to do more checks. You may need to install more packages from the web for your application and you can see how that is done above, too. You now have a working pipeline for pushing an application package to an S3 bucket on every new push.

All of the code in this tutorial can be found in this repo.