How to Continuously Deploy a Heroku App with Azure DevOps

If you create Heroku apps, you know that the only built-in options for deployment are 1) the Heroku CLI or 2) integration with a github repo. If you don’t want your application’s code to be publicly accessible, this results in a tedious routine of manually pushing your changes to heroku. With Azure DevOps, you can create flexible build pipelines to automate various tasks: creating envrionments, creating builds, testing, creating binaries, publishing artifacts, etc. In this post, I will go through the steps to create a simple build pipeline that automatically pushes the latest code to Heroku when changes are committed to a Azure DevOps repo.

Creating a New Build Pipeline

Creating a project in Azure DevOps gives you more than a private git repository. You also receive tools for tracking work items, creating automated test suites and a flexible way to achieve CICD with build pipelines and artifacts. You can navigate to your project’s build piplelines by clicking on the blue rocketship icon. If you have never created a build pipeline for this project, you will be directed to create one (Figure 1). If you would like to create a new build pipeline, select New -> New build pipeline (Figure 2):

First build pipeline

Figure 1 - First build pipeline

New build pipeline

Figure 2 - New build pipeline

First, you must select the type of repository that will be feeding into the pipeline. Since Azure Repos Git is the default value, click Continue (Figure 3):

Select a source repository

Figure 3 - Select a source repository

Next, you are asked to select a template. You can find templates for many technologies/tools, but since we are creating a simple CD process, scroll to the bottom and select Empty pipeline (Figure 4):

Select a templete

Figure 4 - Select a Template

You should see the empty pipeline shown in Figure 5. Notice that you are currently viewing the Tasks section of the pipeline. Change the name to “Deploy to Heroku” or anything else you like. You can leave the value for Agent pool as Hosted VS2017:

Empty pipeline

Figure 5 - Empty pipeline

Enabling Continous Integration

Currently, this pipeline is not configured to run in response to any trigger. In fact, it will never run unless you setup a schedule or enable continuous integration. To do so, Click Triggers and select the checkbox for Enable continuous integration as shown in Figure 6.

Trigger settings

Figure 6 - Trigger settings

Now this pipeline will run whenever a commit is made to the master branch of your repo. You can make this behavior more fine-grained if you wish, by selecting a different branch or adding a path filter in the Triggers section of the pipeline.

Obtaining a Heroku API Token

In order to make code changes to your Heroku app, you must authenticate yourself. Normally this is handled for you by the Heroku CLI. When you first run heroku login an API token is generated and stored in a ~/.netrc file on your machine. To see the value of your API token, you can run heroku auth:token or cat ~/.netrc. This default token is silently used for all further CLI interactions with Heroku, and it can also be used to push changes from our build pipleline.

However, it may not be the best token to use for this since the default token expires one year after being generated, and will also be immediately expired if you run heroku logout. Since we do not want our build pipeline to fail unexpectedly when the API token expires, you can create a token which never expires with the heroku authorizations:create command. For more information on Heroku API tokens, please read this support article.

I suggest creating a dedicated API token for automated deployments. You can also add a helpful description to track the intended use as shown below. For information on the possible options/values with the heroku authorizations:create command, please see the official docs.

$ heroku authorizations:create --description="azure cd token" --short
  787f6805-b74a-45a0-bc58-f6a187bacff2
$ heroku authorizations
  Heroku CLI             5b570a3c-3d2a-4b64-af97-7c1c69fd8f2e  global
  azure cd token         787f6805-b74a-45a0-bc58-f6a187bacff2  global

The API tokens above are not real, so please do not try to use them. Generate an API token for your account (or use your default token) and have the value ready for the next step in the build pipeline.

Creating a Pipeline Variable

Ok, back at the Azure Build Pipeline, click Variables and create a new Pipeline variable as shown in Figure 7. I recommend using a simple, short name for any build variables, so I named mine pat (personal access token).

IMPORTANT! Since exposing the API token would be no different than exposing the password to your Heroku account, make sure to click the lock icon to make the variable secret as shown in Figure 8. This will prevent the API token from appearing in build logs.

Create pipeline variable for API token

Figure 7 - Create pipeline variable for API token

Make pipeline variable secret

Figure 8 - Make pipeline variable secret

Configuring the Build Task

It might seem like we’ve done a lot of work up to this point, but right now our pipeline doesn’t actually do anything. Click on Tasks to return to the empty build template. As shown in Figure 9, click on the symbol to add a task to Agent job 1:

Add a build task to Agent job 1

Figure 9 - Add a build task to Agent job 1

The list of tasks is pretty huge, so filter it by typing “power” or “powershell” into the search box as shown in Figure 10. Then, click the Add button to (you guessed it) add a blank Powershell task to Agent job 1:

Add a Powershell task

Figure 10 - Add a Powershell task

This task requires either a filepath to a Powershell script or an inline Powershel script that we wish to execute when the build is triggered. Click the radio button for Inline as shown in Figure 11:

Select inline for script type

Figure 11 - Select inline for script type

What do we need our script to do, exactly? In order to push our updated code to Heroku, our script needs to do three things:

  1. Checkout the branch which was just updated
  2. Add a remote for our heroku git repo
  3. Push the current version of the updated branch to the heroku remote (authenticating our account with the API token we added as a pipeline variable).

Using Predefined Build Variables

Azure DevOps provides a rich set of predefined variables which can be referenced in build pipelines. For our Powershell script, we need to reference the name of the branch which was updated, and luckily there is a build variable which contains that exact information.

Build.SourceBranchName is the name of the branch the build was queued for ... The last path segment in the ref. For example, in refs/heads/master this value is master.

We can insert build or pipeline variables into our Powershell script by wrapping the name of the variable in $().

Finalizing the Powershell Script

We finally have everything we need to create a Powershell script that performs the three steps we defined in the previous section. If the name of your Heroku app is test_app and your pipeline variable containing your Heroku API token is named pat, you would enter the following three lines into the Powershell task as shown in Figure 12 (obviously, modify the script to match the name of your Heroku app and pipeline variable):

git checkout $(Build.SourceBranchName)
git remote add heroku https://heroku:$(pat)@git.heroku.com/test_app.git
git push heroku $(Build.SourceBranchName)
Inline Powershell script to push changes to Heroku

Figure 12 - Inline Powershell script to push changes to Heroku

It’s a good idea to doublecheck your work at this point. When you are confident that everyting is configured correctly, Save & queue your build as shown in Figure 13:

Save pipeline and queue build

Figure 13 - Save pipeline and queue build

Verify Deployment

After the build is queued, click the link that appears, This will take you to build logs which update live as the deployment is running. If everything was configured correctly, you should eventually see that every step of the build pupeline succeeded as shown in Figure 14:

Successful build logs

Figure 14 - Successful build logs

To see detailed logs for the Heroku deployment script, click on Powershell Script (or download the logs by clicking on the icon that appears to the far right of the words Powershell Script). Scroll to the bottom of the log in the popup that appears and you should see something similar to Figure 15:

Heroku deployment logs

Figure 15 - Heroku deployment logs

Note the revision number (v60) and commit ID (3cced9be) in the Heroku deployment logs from Azure DevOps. You can confirm these same details in the Activity stream for your Heroku app (Figure 16):

Heroku activity stream

Figure 16 - Heroku activity stream

Conclusion

To sum up, if you would like to create a CICD process for your Heroku app, Azure DevOps makes the process quite simple. The most important points to remember are:

  • Enable continuous integration in the Triggers section of your build pipeline. If you forget to do this, your build process will fail to trigger when a new commit is made.
  • Create a Heroku API token that is only used for authenticating automated deployment tasks.
  • Add the API token as a secret variable to your build pipeline to prevent the token from being displayed in log messages.
  • Inspect the log messages for your build if any errors occur.
I hope this post was helpful to you, let me know if you have any questions or suggestions in the commments!