Continuous Integration
Developer Tools

Implement Continuous Deployment with GitHub Actions

Looking to implementing continuous deployment on our platform? Learn how to set up this service using a Divio application with the Divio API and GitHub Actions.

Mebrahtu Zemui Tesfazghi

Mebrahtu Zemui Tesfazghi

Community Manager

Continuous Integration and Continuous Delivery (CI/CD) are critical elements in modern software development, forming a pipeline that enables the automating testing, delivery, and deployment of software features without the need for manual intervention.

A CI/CD pipeline delivers plenty of value for developers – it speeds up feature delivery and iteration by providing fast feedback on the outcome of code changes, and allows developers to focus on writing code.

GitHub Actions provide an excellent solution for getting started with CI/CD without needing to establish separate infrastructure. Developers can set up a full-featured CI/CD pipeline alongside their GitHub repository, and can take advantage of a diverse selection of integrations and tools via the GitHub Marketplace.

Since Divio projects can integrate directly with GitHub, they can also take advantage of the benefits of having a CI/CD pipeline as well.

In this article, we will:

  • create a Divio project, with source code stored in a GitHub repository

  • configure a GitHub Actions workflow that will deploy new code changes (via pull requests) to a development stage for review, and ultimately to a production stage for approved changes.

Prerequisites

This article assumes the user is familiar with Git, GitHub and the basics of the Divio platform. It will also be helpful to be familiar with GitHub Actions and some of the basic terms and principles of CI/CD. For the example application, NodeJS and npm will need to be installed, but any language or framework that can run in a Docker container and return HTTP responses will suffice.

It’s also necessary to have a GitHub account configured with an SSH key, and have a Divio account. (If you're new to Divio,let's talk!)

A note on Continuous Delivery vs. Continuous Deployment

The terms “continuous delivery” and “continuous deployment” are often used interchangeably to refer to the “CD” in “CI/CD”. However, they actually refer to meaningfully different behaviors in how software is deployed to live environments. Continuous delivery is the actual term that CD refers to; it represents a workflow where new code changes are continuously delivered to a target environment, but not deployed. This distinction is important: code is staged for deployment, but requires some manual intervention or external trigger to proceed with an actual deployment. Conversely, continuous deployment refers to a workflow where any code changes or features that pass automated testing are automatically deployed to the target environment without intervention.

In this tutorial, we’ll be using a form of continuous delivery: pull requests will automatically deploy new code to the development environment in Divio. Once the PR is approved and merged, then a deployment to the production environment will proceed.

Create a GitHub repository

Start by creating a new repository on GitHub. This will hold the codebase of the Divio project and is where the GitHub Actions workflow will be configured. For the sake of example, the tutorial will use a very basic Next.js  example site running in a Docker container. The name of the repository doesn’t matter; as long as it is considered valid by GitHub.

The new repository should be initiated with a README.md file. After it’s created, clone it locally to your development machine, enter the directory, and bootstrap a new Next.js project with the following commands:

  1. npm init -y

  2. npm install –save next react react-dom

Next, update the scripts block in the package.json file:

```

"scripts": {

  "dev": "next",

  "build": "next build",

  "start": "next start"

}

```

Create a new directory called pages, and add an index.js file:

```

// pages/index.js


import React from 'react';



const Home = () => (



   <div>

<h1>Welcome to Next.js!</h1>

  </div>

);



export default Home;
```

Next, create a Dockerfile in the root of the project:

```

# Dockerfile



# Set the base image
FROM node:18-alpine


# Set the working directory


WORKDIR /app


# Copy package.json and package-lock.json
COPY package*.json ./


# Install dependencies
RUN npm install


# Copy the rest of your app's source code
COPY . .


# Build the app
RUN npm run build


# Expose the port
EXPOSE 3000


# Start the app
CMD [ "npm", "start" ]
```

Finally, build and launch the container to test it:

```

$ docker build -t <project-name> . 

```

```

$ docker run -p 3000:3000 <project-name>
```

Assuming everything completed successfully, the following should load at http://localhost:3000 in a browser window:

welcome-to-next-js-screenshotWith a very basic app set up, the next steps will be to create a Divio project, and then the GitHub Actions workflow.

Create a Divio project

In the Divio Control Panel, create a new project. Choose an appropriate name for the project; it may be helpful to choose the same name as the repository and the docker container for continuity. Make the following selections for configuration:

  • Stack: “Build your own”

  • Additional Components: “None”

  • Additional Boilerplate: “None”

  • Repository manager: “Custom”

Choosing “Custom” will prompt an input box asking for the default branch, as well as the repository url. Note that this url is not what’s present in the URL bar of the browser, it’s the URL (HTTPS or SSH) for cloning the repository:

Clone ScreenshotThe Divio documentation explains in more detail exactly how to set up external Git repository integration. Assuming SSH is chosen, a new key will be generated and instructions will be provided to set up the key as a “Deploy Key” in GitHub. Once the key has been created in GitHub, clicking “Continue” in the Control Panel prompt will kick-off a test git pull to see if Divio can communicate with the repository. Once that is completed, project creation can proceed.

NOTE: This process will likely push an empty Dockerfile to the remote repo. This should be easily cleared up in a merge with the local code changes from the previous section.

Set up a webhook

A webhook allows GitHub to send a signal to the Divio Control Panel when the repository is updated.

In your Divio project's Repository view, go to Webhook and choose GitHub.

Copy the Webhook URL and the Webhook shared secret – these will need to be added to the GitHub repository.

In your GitHub repository, go to the repository's Settings > Webhooks > Add webhook, and:

  • Add the Webhook URL to the Payload URL field

  • Leave the Content type field as is

  • Add the Webhook shared secret to the Secret field

  • Select Just the Push event and hit Add webhook.

The GitHub repository is now properly configured to communicate with the Divio Control Panel.

Collect environment-specific information from the Divio project

This step will require using a Divio Access Token to make direct calls to the Divio API. Initially, the calls will be to get some information about specific project environments. Later, this information will be used in the GItHub Actions workflow.

Two items are needed:

There are different applications available to make HTTP calls, but most everyone should have access to curl via the terminal on their development machine. The first step is to create environment variables for the two values from the previous step:

  1. $ export API_TOKEN=<divio_access_token_value>

  2. $ export PROJECT_SLUG=<project_name>

Now, query the API to get the application UUID:

$ curl https://api.divio.com/apps/v3/applications/\?slug\=$PROJECT_SLUG -H "Authorization: Token $API_TOKEN"

This step will require using a Divio Access Token to make direct calls to the Divio API. Initially, the calls will be to get some information about specific project environments. Later, this information will be used in the GItHub Actions workflow.

Two items are needed:

There are different applications available to make HTTP calls, but most everyone should have access to curl via the terminal on their development machine. The first step is to create environment variables for the two values from the previous step:

  1. $ export API_TOKEN=<divio_access_token_value>

  2. $ export PROJECT_SLUG=<project_name>

Now, query the API to get the application UUID:

$ curl https://api.divio.com/apps/v3/applications/\?slug\=$PROJECT_SLUG -H "Authorization: Token $API_TOKEN"

Configure the GitHub Actions workflow

With the environment UUIDs, the actual deployment workflow for GitHub Actions can now be configured.

Set the secret credentials

Note: the Divio Access Token is a sensitive credential and provides access to the Divio account. It should never be exposed publicly or in plaintext. GitHub offers a secure way of storing credentials in the repository settings.

It's also good practice to store the other details, such as environment UUIDs, as repository secrets.

In the GitHub repository, find Settings > Secrets > Secrets and variables > Actions.

Add the three values obtained above:

  • API_TOKEN (the Divio access token)

  • TEST_ENVIRONMENT_UUID

  • LIVE_ENVIRONMENT_UUID

Configure the YAML file

GitHub automatically knows to handle YAML files placed in the .github/workflows/ directory as Actions workflows(assuming they are configured correctly). To provide “continuous delivery” functionality will require two separate workflow configurations; one for the test environment and one for the live environment.

Run the following commands in the root of the repository:

  1. $ mkdir - p .github/workflows

  2. $ touch .github/workflows/{test,live}.yml

This will create the GitHub Actions configuration directory, and the empty workflow files

Before adding the workflow configuration, it will be helpful to understand how the workflows will behave and what the objectives are:

  • The “test” workflow will trigger whenever a pull request is opened against the default branch: main

  • The test workflow will check-out the code changes, then deploy them to the test environment by making the appropriate POST call to the webhook. All of this will occur automatically.

  • If the pull request is “approved”, which equates to it being closed and merged, then the “live” workflow will trigger, deploying code changes to the live environment by making the appropriate POST call to the webhook. All of this will occur automatically.

The following is the configuration for the test workflow, test.yml:

```
name: Deploy Test


on:
 pull_request:
branches:
   - main


jobs:
  deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
   - name: Checkout repo
     uses: actions/checkout@v3
 - name: Deploy test app
     run: |
       curl -X POST --data "environment=${{ secrets.TEST_ENVIRONMENT_UUID }}" --header “Authorization: Token ${{ secrets.API_TOKEN }}" https://api.divio.com/apps/v3/deployments/

```

Next, the live workflow, live.yml:

```
name: Deploy Live


on:
  pull_request:
types: [closed]
branches:
   - main


jobs:
  deploy:
name: Deploy
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
   - name: Checkout repo
     uses: actions/checkout@v3

   - name: Deploy live app
     run: |
       curl -X POST --data "environment=${{ secrets.LIVE_ENVIRONMENT_UUID }}" --header “Authorization: Token ${{ secrets.API_TOKEN }}" https://api.divio.com/apps/v3/deployments/
```

Since these workflows only trigger for pull requests, and workflow configurations typically need to be in the default branch to be utilized, the easiest way to proceed is to push the initial code and configuration directly to the main branch. In the context of a tutorial, this is fine, but for a real application direct-to-main pushes are often prevented via repository configuration.

Testing the Continuous Delivery workflow

The final step is to actually make a change to the application, and validate that the continuous delivery pipeline functions as expected.

  1. Use the Divio control panel to change the Git branch of the test environment to “develop”

  2. Create a new local branch named develop and switch to it.

  3. Make a quick change to the index.js file to change or add to the “Welcome to Next.js” message that’s displayed.

  4. Commit the change and push it, creating a remote copy of the local branch.

  5. Go to the GitHub repository and open a pull request against the main branch.

Once the pull request is completed, the test workflow should be initiated. Once it completes successfully, visit the Divo control panel to confirm that the application has been deployed to the test environment. If everything looks good, merge the PR. Once it successfully merges, the live workflow should kick off, and the application will be deployed to production.

Summary

This article showed how Divio can be used with GitHub and GitHub Actions to create a complete CI/CD pipeline for automated testing and delivery of software applications. Although this was a relatively simple application and CI/CD implementation, GitHub Actions provides a wealth of options and functionality that can be added to CI/CD workflows. In this specific case, the application might benefit from some unit testing and linting before it’s deployed to the test environment. Going further, users could potentially add API calls in the workflow to dynamically create and tear down ephemeral test environments for specific branches, rather than depending on a fixed branch naming convention.

Ultimately, GitHub Actions enables Divio users to take full-advantage of a batteries-included CI/CD solution without needing to manage infrastructure.

For further reading refer to How to use the Divio API and GitHub Actions Documentation.

Interested to learn more about the Divio PaaS and how it can support your web applications? Let's talk!

Keep up-to-date with us on LinkedIn and X/Twitter. Here, we share exclusive insights, company news, and more!