Skip to main content

CI/CD Flow

CI/CD Flow: Jenkins


Jenkins Jobs

build-test-frontend

PropertyDetails
Triggered Bypush to the main branch
DescriptionThis job installs dependencies, runs unit tests, and builds the React frontend for deployment.

Pipeline Code:

pipeline {
agent any
stages {
stage('Display Parameter') {
steps {
echo "Received Branch Name: ${params.BRANCH_NAME}"
}
}

stage('Checkout') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "${params.BRANCH_NAME}"]],
userRemoteConfigs: [[
url: 'git@github.com:ComDeall/comdeall-v2.git',
credentialsId: 'jenkins-pvt-key'
]]
])
}
}

stage('List files') {
steps {
sh """
ls -la
"""
}
}

stage('Install deps') {
steps {
sh """
yarn
"""
}
}

stage('Build test') {
steps {
dir("apps/comdeall-admin") {
sh """
cp /home/management-user/env/prod/admin.env .env
yarn build
ls -la
"""
}
}
}
}

post {
success {
slackSend(
color: 'good',
message: "Build Successful: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}

failure {
slackSend(
color: 'danger',
message: "Build Failed: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}

unstable {
slackSend(
color: 'warning',
message: "Build Unstable: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}

aborted {
slackSend(
color: '#808080',
message: "Build Aborted: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}

cleanup {
cleanWs()
sh "yarn cache clean -f"
}
}
}


build-test-backend

PropertyDetails
Triggered Bypush to the main branch
DescriptionThis job installs dependencies, runs unit tests, and builds the Nest backend for deployment.

Pipeline Code:

pipeline {
agent any
stages {
stage('Display Parameter') {
steps {
echo "Received Branch Name: ${params.BRANCH_NAME}"
}
}
stage('Checkout') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "${params.BRANCH_NAME}"]],
userRemoteConfigs: [[
url: 'git@github.com:ComDeall/comdeall-v2.git',
credentialsId: 'jenkins-pvt-key'
]]
])
}
}
stage('List files') {
steps {
sh """
ls -la
"""
}
}
stage('Install deps') {
steps {
sh """
yarn
"""
}
}
stage('Build test') {
steps {
sh """
cp /home/management-user/env/prod/backend.env apps/backend/.env
yarn backend:prisma-generate
yarn backend:build
ls -la
"""
}
}
}
post {
success {
slackSend(
color: 'good',
message: "Build Successful: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
failure {
slackSend(
color: 'danger',
message: "Build Failed: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
unstable {
slackSend(
color: 'warning',
message: "Build Unstable: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
aborted {
slackSend(
color: '#808080',
message: "Build Aborted: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
cleanup {
cleanWs() // Clean workspace
sh "yarn cache clean -f"
}
}
}


build-test-all

PropertyDetails
Triggered Bypush to the main branch
DescriptionThis job installs dependencies, runs unit tests, and builds both the frontend and backend for deployment.

Pipeline Code:

pipeline {
agent any
stages {
stage('Display Parameter') {
steps {
echo "Received Branch Name: ${params.BRANCH_NAME}"
}
}
stage('Checkout') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "${params.BRANCH_NAME}"]],
userRemoteConfigs: [[
url: 'git@github.com:ComDeall/comdeall-v2.git',
credentialsId: 'jenkins-pvt-key'
]]
])
}
}
stage('List files') {
steps {
sh """
ls -la
"""
}
}
stage('Install deps') {
steps {
sh """
yarn
"""
}
}
stage('Build test backend') {
steps {
sh """
cp /home/management-user/env/prod/backend.env apps/backend/.env
yarn backend:prisma-generate
yarn backend:build
ls -la
"""
}
}
stage('Build test Admin') {
steps {
dir("apps/comdeall-admin") {
sh """
cp /home/management-user/env/prod/admin.env .env
yarn build
ls -la
"""
}
}
}
}
post {
success {
slackSend(
color: 'good',
message: "Build Successful: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
failure {
slackSend(
color: 'danger',
message: "Build Failed: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
unstable {
slackSend(
color: 'warning',
message: "Build Unstable: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
aborted {
slackSend(
color: '#808080',
message: "Build Aborted: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
cleanup {
cleanWs() // Clean workspace
sh "yarn cache clean -f"
}
}
}


deploy-admin

PropertyDetails
Triggered Bypush to the main branch
DescriptionThis job installs dependencies and deploys the frontend.

Pipeline Code:

pipeline {
agent any
stages {
stage('Deploy Admin') {
steps {
sshagent(['jenkins-pvt-key']) {
sh """
ssh -o StrictHostKeyChecking=no frontend@10.122.0.9 -p 10022 ./update-fe.sh
"""
}
}
}
}
post {
success {
slackSend(
color: 'good',
message: "Build Successful: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
failure {
slackSend(
color: 'danger',
message: "Build Failed: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
unstable {
slackSend(
color: 'warning',
message: "Build Unstable: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
aborted {
slackSend(
color: '#808080',
message: "Build Aborted: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
cleanup {
cleanWs() // Clean the workspace
}
}
}


deploy-backend

PropertyDetails
Triggered Bypush to the main branch
DescriptionThis job installs dependencies and deploys the backend using Kubernetes.

Pipeline Code:

pipeline {
agent any
environment {
REGISTRY_URL = "registry.digitalocean.com"
DOCKER_IMAGE = "registry.digitalocean.com/backend-v2-comdeall-prod/comdeal-be"
DOCKER_TAG = "latest"
}
stages {
stage('Checkout') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'devops-deployment']],
userRemoteConfigs: [[
url: 'git@github.com:ComDeall/comdeall-v2.git',
credentialsId: 'jenkins-pvt-key'
]]
])
}
}
stage('List files') {
steps {
sh """
cp /home/management-user/env/prod/backend.env apps/backend/.env
mkdir -p apps/backend/certificates
cp /home/management-user/comdeall-v2/apps/backend/certificates/AuthKey_2C5NFA7KFC.p8 apps/backend/certificates/AuthKey_2C5NFA7KFC.p8
cp /home/management-user/comdeall-v2/apps/backend/certificates/ca-certificate.crt apps/backend/certificates/ca-certificate.crt
cp /home/management-user/comdeall-v2/apps/backend/certificates/google-account.json apps/backend/certificates/google-account.json
cp /home/management-user/comdeall-v2/apps/backend/certificates/cf-public-key.pem apps/backend/certificates/cf-public-key.pem
ls -la apps/backend/
"""
}
}
stage('Install deps') {
steps {
sh """
yarn
"""
}
}
stage('Build test') {
steps {
sh """
cp /home/management-user/env/prod/backend.env apps/backend/.env
yarn backend:prisma-generate
yarn backend:build
ls -la
"""
}
}
stage('Copy required files') {
steps {
sh """
ls -la node_modules/.prisma
ls -la node_modules/date-fns
cp -r node_modules/date-fns apps/backend/date-fns
cp -r node_modules/.prisma apps/backend/.prisma
ls -la node_modules/@babel
cp -r node_modules/@babel apps/backend/@babel
ls -la
"""
}
}
stage('Get Commit Hash') {
steps {
script {
env.COMMIT_HASH = sh(
script: "git rev-parse --short HEAD",
returnStdout: true
).trim()
}
echo "Commit Hash: ${env.COMMIT_HASH}"
}
}
stage('Docker Login, Build and Push') {
steps {
withCredentials([
string(credentialsId: 'DOCTL_TOKEN', variable: 'DOCTL_TOKEN')
]) {
dir("apps/backend") {
sh """
export DOCTL_ACCESS_TOKEN=${DOCTL_TOKEN}
echo "${DOCTL_TOKEN}" | docker login registry.digitalocean.com -u doctl --password-stdin
docker build -f prod.Dockerfile -t ${DOCKER_IMAGE}:${DOCKER_TAG} .
docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:${COMMIT_HASH}
docker push ${DOCKER_IMAGE}:${COMMIT_HASH}
docker push ${DOCKER_IMAGE}:${DOCKER_TAG}
"""
}
}
}
}
stage('Deploy to k3s') {
steps {
withCredentials([
file(credentialsId: 'comdeall-k3s-cluster-kubeconfig', variable: 'KUBECONFIG'),
string(credentialsId: 'DOCTL_TOKEN', variable: 'DOCTL_TOKEN')
]) {
dir("/home/management-user/comdeall-v2/k3s/apps") {
script {
def currentDate = sh(
script: 'date +"%Y-%m-%d %H:%M:%S"',
returnStdout: true
).trim()
sh "kubectl get nodes"
sh """
export DOCTL_ACCESS_TOKEN=${DOCTL_TOKEN}
echo "${DOCTL_TOKEN}" | docker login registry.digitalocean.com -u doctl --password-stdin
if kubectl get secret docker-creds -n comdeall >/dev/null 2>&1; then
echo "Deleting existing docker-creds secret..."
kubectl delete secret docker-creds -n comdeall
else
echo "docker-creds secret not found. Skipping deletion."
fi
kubectl create secret docker-registry docker-creds \
--namespace comdeall \
--docker-server=${REGISTRY_URL} \
--docker-username=doctl \
--docker-password=${DOCTL_TOKEN}
kubectl apply -f backend/configmap.yml
kubectl apply -f backend/manifest.yml
kubectl patch deployment backend-prod -n comdeall \
-p '{"spec":{"template":{"metadata":{"annotations":{"kubernetes.io/change-cause":"Triggered rolling update ${currentDate}"}}}}}'
kubectl apply -f backend/manifest.yml
kubectl rollout status deployment/backend-prod -n comdeall
"""
}
}
}
}
}
}
post {
success {
slackSend(
color: 'good',
message: "Build Successful: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
failure {
slackSend(
color: 'danger',
message: "Build Failed: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
unstable {
slackSend(
color: 'warning',
message: "Build Unstable: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
aborted {
slackSend(
color: '#808080',
message: "Build Aborted: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
cleanup {
cleanWs()
sh "docker image prune -a -f"
sh "docker system prune -f"
}
}
}


deploy-hasura

PropertyDetails
Triggered Bypush to the main branch
DescriptionThis job installs dependencies, deploys hasura using Kubernetes and runs the migrations.

Pipeline Code:

pipeline {
agent any
environment {
REGISTRY_URL = "registry.digitalocean.com"
DOCKER_IMAGE = "registry.digitalocean.com/backend-v2-comdeall-prod/comdeal-be"
DOCKER_TAG = "latest"
}
stages {
stage('Checkout') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'devops-deployment']],
userRemoteConfigs: [[
url: 'git@github.com:ComDeall/comdeall-v2.git',
credentialsId: 'jenkins-pvt-key'
]]
])
}
}
stage('List files') {
steps {
sh """
cp /home/management-user/env/prod/backend.env apps/backend/.env
ls -la apps/backend/
"""
}
}
stage('Deploy to k3s') {
steps {
withCredentials([
file(credentialsId: 'comdeall-k3s-cluster-kubeconfig', variable: 'KUBECONFIG'),
string(credentialsId: 'DOCTL_TOKEN', variable: 'DOCTL_TOKEN')
]) {
script {
dir("/home/management-user/comdeall-v2/k3s/apps") {
def currentDate = sh(script: 'date +"%Y-%m-%d %H:%M:%S"', returnStdout: true).trim()
sh "kubectl get nodes"
sh """
export DOCTL_ACCESS_TOKEN=${DOCTL_TOKEN}
echo "${DOCTL_TOKEN}" | docker login registry.digitalocean.com -u doctl --password-stdin
if kubectl get secret docker-creds -n comdeall >/dev/null 2>&1; then
echo "Deleting existing docker-creds secret..."
kubectl delete secret docker-creds -n comdeall
else
echo "docker-creds secret not found. Skipping deletion."
fi
kubectl create secret docker-registry docker-creds \
--namespace comdeall \
--docker-server=${REGISTRY_URL} \
--docker-username=doctl \
--docker-password=${DOCTL_TOKEN}
kubectl apply -f backend/configmap.yml
kubectl apply -f backend/hasura-manifest.yml
kubectl patch deployment hasura-graphql -n comdeall \
-p '{"spec":{"template":{"metadata":{"annotations":{"kubernetes.io/change-cause":"Triggered rolling update ${currentDate}"}}}}}'
kubectl apply -f backend/hasura-manifest.yml
kubectl rollout status deployment/hasura-graphql -n comdeall
"""
}
}
}
}
}
stage('Apply migrations') {
steps {
dir("/home/management-user/comdeall-v2/apps/backend/hasura") {
sh '''
secret='--admin-secret='$( npm run --silent hasura:secret )
hasura metadata apply $secret
hasura migrate apply --database-name comdeall $secret
hasura metadata apply $secret
hasura metadata reload $secret
ls -la
'''
}
}
}
}
post {
success {
slackSend(
color: 'good',
message: "Build Successful: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
failure {
slackSend(
color: 'danger',
message: "Build Failed: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
unstable {
slackSend(
color: 'warning',
message: "Build Unstable: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
aborted {
slackSend(
color: '#808080',
message: "Build Aborted: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
cleanup {
cleanWs()
sh "docker image prune -a -f"
sh "docker system prune -f"
}
}
}


deploy-docs

PropertyDetails
Triggered Bypush to the main branch
DescriptionThis job deploys the docusaurus application.

Pipeline Code:

pipeline {
agent any
stages {
stage('Deploy Docs') {
steps {
sshagent(['jenkins-pvt-key']) {
sh "ssh -o StrictHostKeyChecking=no frontend@10.122.0.9 -p 10022 ./update-docs.sh"
}
}
}
}
post {
success {
slackSend(
color: 'good',
message: "Build Successful: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
failure {
slackSend(
color: 'danger',
message: "Build Failed: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
unstable {
slackSend(
color: 'warning',
message: "Build Unstable: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
aborted {
slackSend(
color: '#808080',
message: "Build Aborted: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
)
}
cleanup {
cleanWs()
}
}
}