9# How to add Continuous Development with Github Actions in CI/CD

Alejo Cain
6 min readJun 30, 2023

--

In the previous posts we went over how to do the “CI” continuous Integration for our drag and drop robot builder application. And we add SSH keys to make logging in less of a hassle and a bit safer.

Although this is really cool, the code is still not running on the digital ocean droplet. For the front-end image we will save it in the digital ocean register since the first one is free ;) When we get to the back-end we can see if can get away with putting that image on a fresh docker account and use it as our free image there?? Time will tell.

We use all the free tiers services here!

Step 1 Creating a Container Registry in Digital Ocean

Login to digital ocean web page and in the menu on the left side click on the Container Registry.

Click on the big blue button, Create a Container Registry.

Enter a unique name and remember you can’t change it later so be mindful.

Since we are in Northern Europe the closest Data-center is Amsterdam, and choose the Starter plan because it is free.

Step 2 Installing doctl on ubntu 20.04

doctl makes it possible for you to interact with the DigitalOcean API through the terminal.

Open a terminal and put in the following command

sudo snap install doctl 
enter your password:
doctl v1.96.1 from DigitalOcean✓ installed

Step 3 Use the API token to grant account access to doctl

doctl auth init
Please authenticate doctl for use with your DigitalOcean account. You can generate a token in the control panel at https://cloud.digitalocean.com/account/api/tokens

Click on the link https://cloud.digitalocean.com/account/api/tokens, and don’t close that terminal.

You will see the following screen:

Generate New Token

Give your token a name that you can remember.

Copy the generated token into your terminal that is still open.

 Enter your access token:  ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

Validating token... ✔

Step 4: validate that doctl is working

if Succesfull you should see the following output:

 Email                      Droplet Limit    Email Verified    UUID                                        Status
sammy@example.org 10 true 3a56c5e109736b50e823eaebca85708ca0e5087c active

Step 5: storing API token in Github Secrets

Now it is time to add the API token to your Github Secrets

Navigate to your repository and go to the settings tab on the right. And in the left hand side menu click on secrets. And now click on the big green button New repository secret

Secretts and variables

Now add your API token, for the sake of your mental sanity give it the same token name as you have given it in digital ocean.

Adding a new secret

Step 6. Updating the workflow file

We will now have to add a few steps to the workflow file so our github actions can login with dotcl, set the ssh key in the runner (github vm) and last but not least pull the image from our the digitalocean registry that you already pushed in. And finally run the docker image!!!

name: Deployment to Digital Ocean

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 20

- name: Cache Node.js modules
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-node-
${{ runner.OS }}-
- name: Install Dependencies
run: npm install
working-directory: <projectFolder-name>

- name: Build
run: npm run build
working-directory: <projectFolder-name>

- name: Login to DigitalOcean Container Registry
uses: docker/login-action@v1
with:
registry: registry.digitalocean.com
username: ${{ secrets.DO_API_TOKEN }}
password: ${{ secrets.DO_API_TOKEN }}

- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: <projectFolder-name>
push: true
tags: registry.digitalocean.com/<digitaloceanRegistry>/<image-name>:${{ github.sha }}

deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DO_API_TOKEN }}

- name: Set up SSH agent
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
eval $(ssh-agent)
ssh-add ~/.ssh/id_rsa
- name: Login to DigitalOcean Container Registry
run: |
doctl registry login
- name: Docker Pull and Run on Droplet
run: |
ssh -o StrictHostKeyChecking=no <user>@ip4> 'docker pull registry.digitalocean.com/<digitaloceanRegistry>/<image-name>:${{ github.sha }}'
ssh -o StrictHostKeyChecking=no <user>@ip4> 'docker run -d -p 80:80 registry.digitalocean.com/<digitaloceanRegistry>/<image-name>${{ github.sha }}'

Step 7 Installing doctl & adding sudo rights to user

If this workflow is not working for you make sure you login to your server and install docker according to this guide. And also install doctl on your server for the user account you are using in your workflow. So if you login with:

ssh john@132.xxx.xxx.xx  

Make sure that the user ‘john’ is added as a sudoer with this command:

sudo usermod -aG docker john

! important! for this workflow to work for you run the following commands in the terminal of your server (droplet)

sudo mkdir -p /root/.config
sudo chown -R root:root /root/.config

Step 8 Let it run and enjoy

After investing a whole day in setting up this deployment process, you can now sit back and witness the magic in action. As a reader, you might have already identified areas for improvement. For instance, consider running the pipeline only when merging changes to the main branch, and executing the build job with every commit while restricting the deploy step to merges into the main branch. This optimization ensures a smoother workflow and reduces unnecessary deployments. Rest assured, if any challenges arise, you can always upgrade and fine-tune the process accordingly.

Now, take a moment to appreciate the fruits of your labor. Navigate to your subdomain, and you should see your container running flawlessly. If, for any reason, you don’t see it working as expected, double-check your Vite configuration file. Ensure that the port specified in the configuration file matches the port used in the “docker run -p 80:80” command. By aligning these ports, your subdomain will seamlessly listen on the default port 80, providing a clean and professional appearance. Say goodbye to awkward URLs like “subdomain.domain:8000”!

Embrace the power of Continuous Deployment and relish the convenience it brings to your development workflow. Feel free to experiment, share your experiences, and gather feedback from the community. Remember, this is just the beginning of an exciting journey toward efficient and automated software deployment.

Now, sit back, relax, and let your deployment process do the heavy lifting. Enjoy the seamless and effortless deployment experience you’ve worked hard to create!

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
host: true,
strictPort: true,
port: 80
}
})

Pros

  • GitHub Actions is cool
  • Saves deployment time in the future
  • Future team members can contribute to project without needing to understand the infrastructure
  • Repeatability of deployment
  • Greater appreciation for dedicated devops engineers

Cons

  • Documentation is fragmented around the internet
  • Not all actions are made with the same level of detail
  • Adding a passphrase to ssh-key is a pain!
  • Bug hunting is not ideal

Congratulations! You have successfully set up Continuous Deployment for our project. Please reach out if you have any tips or tricks to improve this process so we can all learn from it.

Thank you for reading! If you found this article helpful, consider supporting me on my Patreon page at patreon.com/OjisanTech. Your support will contribute to covering server costs and keeping me fueled with snacks.

--

--

Alejo Cain

Software engineer that likes to talk about tools to build robots