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
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.
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!)
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.
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:
npm init -y
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:
With a very basic app set up, the next steps will be to create a Divio project, and then the GitHub Actions workflow.
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:
The 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.
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.
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:
The Divio Access Token. It can be obtained from the account settings on the Divio Control Panel.
The project's slug, which can be copied from the Control Panel.
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:
$ export API_TOKEN=<divio_access_token_value>
$ 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:
The Divio Access Token. It can be obtained from the account settings on the Divio Control Panel.
The project's slug, which can be copied from the Control Panel.
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:
$ export API_TOKEN=<divio_access_token_value>
$ 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"
With the environment UUIDs, the actual deployment workflow for GitHub Actions can now be configured.
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
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:
$ mkdir - p .github/workflows
$ 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.
The final step is to actually make a change to the application, and validate that the continuous delivery pipeline functions as expected.
Use the Divio control panel to change the Git branch of the test environment to “develop”
Create a new local branch named develop
and switch to it.
Make a quick change to the index.js
file to change or add to the “Welcome to Next.js” message that’s displayed.
Commit the change and push it, creating a remote copy of the local branch.
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.
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!