Skip to main content

Setting up CI CD for Developers

This tutorial will guide you through the steps to set up a project on the ABAIR CI/CD pipeline. As an example, it uses a node application with the name ABAIR_APP_X.

Prerequisites:

  1. Server:

    • You have a key for your project to SSH into the services VM.
    • An Admin has set up a directory for your project with a deployment script.
  2. Github:

  3. Docker:

    • You have Docker installed on your local machine.
    • You have a Dockerfile in the root of your project that successfully builds an image of your application.
    • This image can be run locally using Docker and accessed over the specified open port.
    • You have been given the Docker Password for the registry.

Steps

1. Add CI/CD Github Actions to your project

Create a .github/workflows/ folder in the root of your project and add the following 2 files

ci.yml

name: Node.js CI

on:
pull_request:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Use Node.js LTS
uses: actions/setup-node@v3
with:
node-version: 18.16.0
- run: |
npm i

cd.yml

name: Publish Docker image

on:
push:
branches:
- main

jobs:
push_to_registry:
name: Push Docker image to Docker Registry
runs-on: ubuntu-latest
environment: prod
steps:
- name: Check out the repo
uses: actions/checkout@v2

- name: Log in to Docker Hub
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
registry: ${{ vars.DOCKER_REGISTRY }}
username: ${{ vars.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: registry.abair.ie:5000/${{ vars.PROJECT_NAME }}

- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
update_ssh:
name: Update running software
needs: "push_to_registry"
runs-on: ubuntu-latest
environment: prod
steps:
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@master
with:
host: ${{ vars.HOST }}
username: ${{ vars.USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ vars.PORT }}
script: bash ${{ vars.DEPLOY_SCRIPT_PATH }}

2. Generate SSH Private Key

Recommended: Use the generate-ssh-key.sh script which automates the steps below.

  1. Sign into the Services VM as a sudo user.
  2. Sign into the services account using sudo su services.
  3. Run the following command to generate the SSH key. Make sure to replace ABAIR_APP_X with the name of your app.
ssh-keygen -t ed25519 -a 100 \
-C "ABAIR_APP_X Github Action" \
-f ~/.ssh/ABAIR_APP_X_ed25519 \
-N ""
  1. Add the public key to authorized_keys so the Services VM accepts connections using this key:
cat ~/.ssh/ABAIR_APP_X_ed25519.pub >> ~/.ssh/authorized_keys
  1. Copy the private key contents to the GitHub prod environment secrets as SSH_PRIVATE_KEY:
cat ~/.ssh/ABAIR_APP_X_ed25519

Copy the entire output (including -----BEGIN OPENSSH PRIVATE KEY----- and -----END OPENSSH PRIVATE KEY-----).

3. Create Environment and Set Secrets/Variables

On the Github page for ABAIR_APP_X:

3.1 Create the prod Environment

  1. Navigate to SettingsEnvironments
  2. Click New environment
  3. Name it prod and click Configure environment

3.2 Add Environment Secrets

In the prod environment settings, under Environment secrets, add:

NameValue
DOCKER_PASSWORD(provided by Admin)
SSH_PRIVATE_KEYPrivate SSH key from the services user in the Services VM

If your project uses external services, add their credentials as secrets too. Common examples:

NameDescription
SUPABASE_SECRET_KEYSupabase service role key (bypasses RLS)
ABAIR_APP_SMTP_PASSWORDSMTP password for sending emails
UNSUBSCRIBE_SECRETHMAC signing secret for secure tokens

3.3 Add Environment Variables

In the prod environment settings, under Environment variables, add:

NameValue
PROJECT_NAMEABAIR_APP_X
DOCKER_REGISTRYregistry.abair.ie:5000
DOCKER_USERNAMEadmin
HOSTsrv.abair.ie
USERNAMEservices
PORT22102
DEPLOY_SCRIPT_PATH/home/services/ABAIR_APP_X/deploy-ABAIR_APP_X.sh

If your project needs environment variables at build time (e.g. Supabase, API URLs), add them as variables too:

NameDescription
NEXT_PUBLIC_SUPABASE_URLSupabase project URL
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYSupabase anonymous/public key
NEXT_PUBLIC_APP_URLPublic URL of your application

3.4 Pass Environment Variables in Workflows

CI: Add an env block to the build step so NEXT_PUBLIC_* variables are available during npm run build:

      - name: Build application
run: npm run build
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ vars.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: ${{ vars.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY }}

CD: Add a build-args block to the Docker build step. Use vars.* for non-sensitive values and secrets.* for credentials:

          build-args: |
NEXT_PUBLIC_SUPABASE_URL=${{ vars.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=${{ vars.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY }}
SUPABASE_SECRET_KEY=${{ secrets.SUPABASE_SECRET_KEY }}

Your Dockerfile must declare matching ARG lines before RUN npm run build for these to take effect:

ARG NEXT_PUBLIC_SUPABASE_URL
ARG NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
ARG SUPABASE_SECRET_KEY

Note: NEXT_PUBLIC_* variables are inlined by Next.js at build time into the client bundle. Server-only secrets (without the NEXT_PUBLIC_ prefix) are only available server-side and should be provided as runtime environment variables or Docker ENV in the production stage of your Dockerfile.

4. Deploy Script

Add the following deploy script to the base of your project for reference. It will be copied by Admin to the Services VM

deploy-ABAIR_APP_X.sh

docker stop ABAIR_APP_X
docker rm ABAIR_APP_X

docker login 10.0.0.2:5000 -u admin -p {see elsewhere}
docker rmi 10.0.0.2:5000/ABAIR_APP_X:main
docker pull 10.0.0.2:5000/ABAIR_APP_X:main

docker run -t -d -p port:port --restart always --name project-name 10.0.0.2:5000/ABAIR_APP_X:main

FAQ

The CI/CD Fails when I add Supabase to my project

This usually means NEXT_PUBLIC_SUPABASE_URL or NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY are not available at build time. Follow Section 3.4 to add the required variables and build-args to your workflows and Dockerfile.

Failing to authorize using publickey

Follow this carefully.

  • Make sure your private SSH key is pasted correctly into the GitHub prod environment secrets as SSH_PRIVATE_KEY. You must copy all contents of the file.
  • Make sure that your ssh key file is named correctly (ABAIR_APP_X_ed25519).
  • Make sure that your public ssh key is added to authorized keys.