From cb9d7cacc70d92906aafdeefdf26fe8d219f4741 Mon Sep 17 00:00:00 2001 From: Dafa Ardiansyah Date: Wed, 30 Apr 2025 07:29:44 +0000 Subject: [PATCH 1/9] ci/cd (jenkinsfile & Docker) : add Jenkinsfile & build.sh --- Jenkinsfile | 116 +++++++++++++++++++++++++++++++ build_push_image_karsajobs_ui.sh | 35 ++++++++++ 2 files changed, 151 insertions(+) create mode 100644 Jenkinsfile create mode 100644 build_push_image_karsajobs_ui.sh diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000..6c06ea814 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,116 @@ +pipeline { + agent any + + tools { + nodejs 'NodeJS 16' // Sesuaikan dengan versi Node.js yang dibutuhkan untuk Vue.js + } + + parameters { + string(name: 'RELEASE_TAG', defaultValue: '', description: 'Release tag to build and deploy (kosongkan untuk menggunakan build number)') + choice(name: 'DEPLOY_ENV', choices: ['development', 'production'], description: 'Environment to deploy') + } + + environment { + DOCKER_HUB_CREDS = credentials('docker-hub') + DOCKER_HUB_PAT = credentials('docker-hub-pat') + DISCORD_WEBHOOK = credentials('discord-notification') + APP_NAME = 'karsajobs-ui' + DOCKER_IMAGE = "${DOCKER_HUB_CREDS_USR}/${APP_NAME}" + IMAGE_TAG = "${params.RELEASE_TAG ? params.RELEASE_TAG : env.BUILD_NUMBER}" + } + + stages { + stage('Checkout') { + steps { + checkout scm + script { + try { + discordSend( + webhookURL: DISCORD_WEBHOOK, + title: "BUILD STARTED: ${env.JOB_NAME} #${env.BUILD_NUMBER}", + description: "Build started for ${env.JOB_NAME} #${env.BUILD_NUMBER}", + link: env.BUILD_URL, + result: 'STARTED' + ) + } catch (Exception e) { + echo "Discord notification failed: ${e.message}" + } + } + } + } + + stage('Lint Dockerfile') { + steps { + sh ''' + wget -O hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64 + chmod +x hadolint + ./hadolint Dockerfile || true + ''' + } + } + + stage('Docker Build') { + steps { + sh "docker build -t ${DOCKER_IMAGE}:${IMAGE_TAG} -t ${DOCKER_IMAGE}:latest ." + } + } + + stage('Docker Push') { + steps { + sh "echo ${DOCKER_HUB_PAT} | docker login -u ${DOCKER_HUB_CREDS_USR} --password-stdin" + sh "docker push ${DOCKER_IMAGE}:${IMAGE_TAG}" + sh "docker push ${DOCKER_IMAGE}:latest" + } + } + } + + post { + success { + script { + try { + discordSend( + webhookURL: DISCORD_WEBHOOK, + title: "BUILD SUCCESSFUL: ${env.JOB_NAME} #${env.BUILD_NUMBER}", + description: "Build completed successfully for ${env.JOB_NAME} #${env.BUILD_NUMBER}", + link: env.BUILD_URL, + result: 'SUCCESS' + ) + } catch (Exception e) { + echo "Discord notification failed: ${e.message}" + } + } + } + failure { + script { + try { + discordSend( + webhookURL: DISCORD_WEBHOOK, + title: "BUILD FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}", + description: "Build failed for ${env.JOB_NAME} #${env.BUILD_NUMBER}", + link: env.BUILD_URL, + result: 'FAILURE' + ) + } catch (Exception e) { + echo "Discord notification failed: ${e.message}" + } + } + } + always { + // Clean up Docker images and builder cache + withEnv([ + "DOCKER_IMG=${DOCKER_IMAGE}", + "IMG_TAG=${IMAGE_TAG}" + ]) { + sh ''' + # Hapus image yang tidak terpakai + docker rmi $DOCKER_IMG:$IMG_TAG || true + docker rmi $DOCKER_IMG:latest || true + + # Bersihkan builder cache untuk mencegah penumpukan storage + docker builder prune -f || true + ''' + } + cleanWs() + } + } +} \ No newline at end of file diff --git a/build_push_image_karsajobs_ui.sh b/build_push_image_karsajobs_ui.sh new file mode 100644 index 000000000..647ca9d37 --- /dev/null +++ b/build_push_image_karsajobs_ui.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Script untuk build dan push Docker image untuk aplikasi frontend Karsa Jobs UI +# Script ini harus dijalankan dari dalam direktori karsajobs/frontend + +# Variabel username untuk Docker Hub +# Ganti dengan username Docker Hub Anda +USERNAME="ardidafa" +# Optional tag, defaultnya adalah latest atau dari parameter pertama script +TAG="${1:-latest}" + +# ============================================================== +# Step 1: Build Docker image dari Dockerfile yang tersedia +# ============================================================== +echo "Building Docker image: ${USERNAME}/karsajobs-ui:${TAG}" +docker build -t ${USERNAME}/karsajobs-ui:${TAG} -t ${USERNAME}/karsajobs-ui:latest . + +# ============================================================== +# Step 2: Login ke Docker Hub menggunakan Personal Access Token +# ============================================================== +echo "Logging in to Docker Hub" +# Menggunakan environment variable DOCKER_HUB_PAT atau PASSWORD_DOCKER_HUB +# Jika menjalankan dari Jenkins, PAT akan disediakan oleh credentials +# Jika menjalankan manual, gunakan PASSWORD_DOCKER_HUB yang di-export sebelumnya +TOKEN="${DOCKER_HUB_PAT:-$PASSWORD_DOCKER_HUB}" +echo $TOKEN | docker login -u ${USERNAME} --password-stdin + +# ============================================================== +# Step 3: Push image ke Docker Hub +# ============================================================== +echo "Pushing image to Docker Hub: ${USERNAME}/karsajobs-ui:${TAG}" +docker push ${USERNAME}/karsajobs-ui:${TAG} +docker push ${USERNAME}/karsajobs-ui:latest + +echo "Process completed successfully!" \ No newline at end of file From 9b288d29a576c3673517a8dcdb6b6351de56eda6 Mon Sep 17 00:00:00 2001 From: Dafa Ardiansyah Date: Fri, 2 May 2025 04:33:06 +0000 Subject: [PATCH 2/9] chore: update .env and build script permissions Update the VUE_APP_BACKEND URL in .env to use localhost instead of a placeholder IP. Additionally, make the build_push_image_karsajobs_ui.sh script executable and remove a redundant comment about Docker Hub username. --- .env | 2 +- build_push_image_karsajobs_ui.sh | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) mode change 100644 => 100755 build_push_image_karsajobs_ui.sh diff --git a/.env b/.env index 4d34703d6..e4a4e6536 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -VUE_APP_BACKEND=http://: \ No newline at end of file +VUE_APP_BACKEND=localhost: \ No newline at end of file diff --git a/build_push_image_karsajobs_ui.sh b/build_push_image_karsajobs_ui.sh old mode 100644 new mode 100755 index 647ca9d37..efe09f286 --- a/build_push_image_karsajobs_ui.sh +++ b/build_push_image_karsajobs_ui.sh @@ -4,7 +4,6 @@ # Script ini harus dijalankan dari dalam direktori karsajobs/frontend # Variabel username untuk Docker Hub -# Ganti dengan username Docker Hub Anda USERNAME="ardidafa" # Optional tag, defaultnya adalah latest atau dari parameter pertama script TAG="${1:-latest}" From cf0783103c4f1dd3398e58b8e76055c395e64487 Mon Sep 17 00:00:00 2001 From: Dafa Ardiansyah Date: Fri, 2 May 2025 04:39:51 +0000 Subject: [PATCH 3/9] ci(Jenkinsfile): install wget before downloading hadolint --- Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile b/Jenkinsfile index 6c06ea814..93387ff7a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -42,6 +42,7 @@ pipeline { stage('Lint Dockerfile') { steps { sh ''' + apt-get update && apt-get install -y wget || true wget -O hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64 chmod +x hadolint ./hadolint Dockerfile || true From 79f5f07f34c0a6d0746a8454437ce3c1918d9905 Mon Sep 17 00:00:00 2001 From: Dafa Ardiansyah Date: Fri, 2 May 2025 04:43:30 +0000 Subject: [PATCH 4/9] ci: replace wget with curl for downloading hadolint --- Jenkinsfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 93387ff7a..46254536f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -42,8 +42,7 @@ pipeline { stage('Lint Dockerfile') { steps { sh ''' - apt-get update && apt-get install -y wget || true - wget -O hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64 + curl -O hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64 chmod +x hadolint ./hadolint Dockerfile || true ''' From 40b2db95371461edbcbef5fa5acefb43a2533ed5 Mon Sep 17 00:00:00 2001 From: Dafa Ardiansyah Date: Fri, 2 May 2025 04:47:18 +0000 Subject: [PATCH 5/9] ci: switch to docker for hadolint in Jenkinsfile --- Jenkinsfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 46254536f..d36423fd8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -42,9 +42,8 @@ pipeline { stage('Lint Dockerfile') { steps { sh ''' - curl -O hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64 - chmod +x hadolint - ./hadolint Dockerfile || true + # Use docker to run hadolint instead of downloading it + docker run --rm -i hadolint/hadolint < Dockerfile || true ''' } } From db25e21b6b94c52c1ae996d46556cb5f1303d12a Mon Sep 17 00:00:00 2001 From: Dafa Ardiansyah Date: Fri, 2 May 2025 04:51:48 +0000 Subject: [PATCH 6/9] ci: update Docker Hub username in Jenkinsfile --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d36423fd8..74f2dbd24 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -15,7 +15,7 @@ pipeline { DOCKER_HUB_PAT = credentials('docker-hub-pat') DISCORD_WEBHOOK = credentials('discord-notification') APP_NAME = 'karsajobs-ui' - DOCKER_IMAGE = "${DOCKER_HUB_CREDS_USR}/${APP_NAME}" + DOCKER_IMAGE = "ardidafa/${APP_NAME}" IMAGE_TAG = "${params.RELEASE_TAG ? params.RELEASE_TAG : env.BUILD_NUMBER}" } @@ -56,7 +56,7 @@ pipeline { stage('Docker Push') { steps { - sh "echo ${DOCKER_HUB_PAT} | docker login -u ${DOCKER_HUB_CREDS_USR} --password-stdin" + sh "echo ${DOCKER_HUB_PAT} | docker login -u ardidafa --password-stdin" sh "docker push ${DOCKER_IMAGE}:${IMAGE_TAG}" sh "docker push ${DOCKER_IMAGE}:latest" } From 718d06e115aa832aca449ebfee6b4a864080cea4 Mon Sep 17 00:00:00 2001 From: Dafa Ardiansyah Date: Fri, 2 May 2025 04:55:35 +0000 Subject: [PATCH 7/9] ci: update NodeJS version to 18 in Jenkinsfile Update the NodeJS version in the Jenkinsfile from 16 to 18 to align with the required version for Vue.js development. --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 74f2dbd24..a1a19f40f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,7 +2,7 @@ pipeline { agent any tools { - nodejs 'NodeJS 16' // Sesuaikan dengan versi Node.js yang dibutuhkan untuk Vue.js + nodejs 'NodeJS 18' // Sesuaikan dengan versi Node.js yang dibutuhkan untuk Vue.js } parameters { From 26872fa073ce9e137dc23ac483d5135fc2467165 Mon Sep 17 00:00:00 2001 From: Dafa Ardiansyah Date: Fri, 2 May 2025 05:42:43 +0000 Subject: [PATCH 8/9] chore: update backend URL in .env file Update the VUE_APP_BACKEND variable to include the full HTTP URL for consistency and clarity in the configuration --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index e4a4e6536..4d34703d6 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -VUE_APP_BACKEND=localhost: \ No newline at end of file +VUE_APP_BACKEND=http://: \ No newline at end of file From a827f3f9230864652e495b699100a70a03bc7f9f Mon Sep 17 00:00:00 2001 From: Dafa Ardiansyah Date: Fri, 2 May 2025 11:40:49 +0000 Subject: [PATCH 9/9] docs(Jenkinsfile): add detailed comments to clarify pipeline steps --- Jenkinsfile | 142 +++++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 67 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a1a19f40f..4127dcb09 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,115 +1,123 @@ -pipeline { - agent any +pipeline { // Mendefinisikan blok pipeline untuk CI/CD Jenkins + agent any // Menjalankan pipeline pada agen Jenkins yang tersedia - tools { - nodejs 'NodeJS 18' // Sesuaikan dengan versi Node.js yang dibutuhkan untuk Vue.js + tools { // Mendefinisikan tools yang dibutuhkan untuk pipeline + nodejs 'NodeJS 18' // Menggunakan Node.js versi 18 untuk aplikasi frontend Vue.js } - parameters { - string(name: 'RELEASE_TAG', defaultValue: '', description: 'Release tag to build and deploy (kosongkan untuk menggunakan build number)') - choice(name: 'DEPLOY_ENV', choices: ['development', 'production'], description: 'Environment to deploy') + parameters { // Mendefinisikan parameter yang dapat diatur saat menjalankan pipeline + string(name: 'RELEASE_TAG', // Parameter string untuk tag rilis + defaultValue: '', + description: 'Release tag to build and deploy (kosongkan untuk menggunakan build number)') + choice(name: 'DEPLOY_ENV', // Parameter pilihan untuk environment deployment + choices: ['development', 'production'], + description: 'Environment to deploy') } - environment { - DOCKER_HUB_CREDS = credentials('docker-hub') - DOCKER_HUB_PAT = credentials('docker-hub-pat') - DISCORD_WEBHOOK = credentials('discord-notification') - APP_NAME = 'karsajobs-ui' - DOCKER_IMAGE = "ardidafa/${APP_NAME}" - IMAGE_TAG = "${params.RELEASE_TAG ? params.RELEASE_TAG : env.BUILD_NUMBER}" - } + environment { // Mendefinisikan variabel lingkungan untuk pipeline + DOCKER_HUB_CREDS = credentials('docker-hub') // Mengambil kredensial Docker Hub dari Jenkins credentials + DOCKER_HUB_PAT = credentials('docker-hub-pat') // Mengambil Personal Access Token Docker Hub + DISCORD_WEBHOOK = credentials('discord-notification') // Mengambil webhook Discord untuk notifikasi + APP_NAME = 'karsajobs-ui' // Nama aplikasi frontend untuk container Docker + DOCKER_IMAGE = "ardidafa/${APP_NAME}" // Nama image Docker yang akan dibuat + IMAGE_TAG = "${params.RELEASE_TAG ? params.RELEASE_TAG : env.BUILD_NUMBER}" // Tag image: gunakan RELEASE_TAG jika ada, jika tidak gunakan nomor build - stages { - stage('Checkout') { - steps { - checkout scm - script { - try { + stages { // Mendefinisikan tahapan-tahapan dalam pipeline + stage('Checkout') { // Tahap mengambil kode dari repositori + steps { // Langkah-langkah yang dijalankan dalam tahap ini + checkout scm // Mengambil kode dari Source Control Management (Git) + script { // Blok script untuk menjalankan kode Groovy + try { // Mencoba mengirim notifikasi Discord discordSend( - webhookURL: DISCORD_WEBHOOK, - title: "BUILD STARTED: ${env.JOB_NAME} #${env.BUILD_NUMBER}", - description: "Build started for ${env.JOB_NAME} #${env.BUILD_NUMBER}", - link: env.BUILD_URL, - result: 'STARTED' + webhookURL: DISCORD_WEBHOOK, // URL webhook Discord untuk notifikasi + title: "BUILD STARTED: ${env.JOB_NAME} #${env.BUILD_NUMBER}", // Judul notifikasi + description: "Build started for ${env.JOB_NAME} #${env.BUILD_NUMBER}", // Deskripsi notifikasi + link: env.BUILD_URL, // Link ke halaman build Jenkins + result: 'STARTED' // Status build: dimulai ) - } catch (Exception e) { - echo "Discord notification failed: ${e.message}" + } catch (Exception e) { // Menangkap error jika notifikasi gagal + echo "Discord notification failed: ${e.message}" // Menampilkan pesan error } } } } - stage('Lint Dockerfile') { + stage('Lint Dockerfile') { // Tahap memeriksa kualitas Dockerfile + // Menjalankan perintah shell multi-baris steps { - sh ''' + sh ''' # Use docker to run hadolint instead of downloading it - docker run --rm -i hadolint/hadolint < Dockerfile || true - ''' + docker run --rm -i hadolint/hadolint < Dockerfile || true + ''' // || true: lanjutkan pipeline meskipun ada warning + // Menjalankan hadolint dalam container Docker } } - stage('Docker Build') { + stage('Docker Build') { // Tahap membangun image Docker untuk frontend steps { - sh "docker build -t ${DOCKER_IMAGE}:${IMAGE_TAG} -t ${DOCKER_IMAGE}:latest ." + sh "docker build -t ${DOCKER_IMAGE}:${IMAGE_TAG} -t ${DOCKER_IMAGE}:latest ." // Membuat image dengan tag versi dan latest } } - stage('Docker Push') { + stage('Docker Push') { // Tahap mengunggah image ke Docker Hub steps { - sh "echo ${DOCKER_HUB_PAT} | docker login -u ardidafa --password-stdin" - sh "docker push ${DOCKER_IMAGE}:${IMAGE_TAG}" - sh "docker push ${DOCKER_IMAGE}:latest" + sh "echo ${DOCKER_HUB_PAT} | docker login -u ardidafa --password-stdin" // Login ke Docker Hub menggunakan PAT + sh "docker push ${DOCKER_IMAGE}:${IMAGE_TAG}" // Mengunggah image dengan tag versi + sh "docker push ${DOCKER_IMAGE}:latest" // Mengunggah image dengan tag latest } } } - post { - success { - script { - try { + post { // Mendefinisikan tindakan setelah pipeline selesai + success { // Tindakan jika pipeline berhasil + script { // Blok script untuk menjalankan kode Groovy + try { // Mencoba mengirim notifikasi Discord discordSend( - webhookURL: DISCORD_WEBHOOK, - title: "BUILD SUCCESSFUL: ${env.JOB_NAME} #${env.BUILD_NUMBER}", - description: "Build completed successfully for ${env.JOB_NAME} #${env.BUILD_NUMBER}", - link: env.BUILD_URL, - result: 'SUCCESS' + webhookURL: DISCORD_WEBHOOK, // URL webhook Discord untuk notifikasi + title: "BUILD SUCCESSFUL: ${env.JOB_NAME} #${env.BUILD_NUMBER}", // Judul notifikasi sukses + description: "Build completed successfully for ${env.JOB_NAME} #${env.BUILD_NUMBER}", // Deskripsi notifikasi + link: env.BUILD_URL, // Link ke halaman build Jenkins + result: 'SUCCESS' // Status build: sukses ) - } catch (Exception e) { - echo "Discord notification failed: ${e.message}" + } catch (Exception e) { // Menangkap error jika notifikasi gagal + echo "Discord notification failed: ${e.message}" // Menampilkan pesan error } } } - failure { - script { - try { + failure { // Tindakan jika pipeline gagal + script { // Blok script untuk menjalankan kode Groovy + try { // Mencoba mengirim notifikasi Discord discordSend( - webhookURL: DISCORD_WEBHOOK, - title: "BUILD FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}", - description: "Build failed for ${env.JOB_NAME} #${env.BUILD_NUMBER}", - link: env.BUILD_URL, - result: 'FAILURE' + webhookURL: DISCORD_WEBHOOK, // URL webhook Discord untuk notifikasi + title: "BUILD FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}", // Judul notifikasi gagal + description: "Build failed for ${env.JOB_NAME} #${env.BUILD_NUMBER}", // Deskripsi notifikasi + link: env.BUILD_URL, // Link ke halaman build Jenkins + result: 'FAILURE' // Status build: gagal ) - } catch (Exception e) { - echo "Discord notification failed: ${e.message}" + } catch (Exception e) { // Menangkap error jika notifikasi gagal + echo "Discord notification failed: ${e.message}" // Menampilkan pesan error } } } - always { + always { // Tindakan yang selalu dijalankan, baik sukses maupun gagal // Clean up Docker images and builder cache - withEnv([ - "DOCKER_IMG=${DOCKER_IMAGE}", - "IMG_TAG=${IMAGE_TAG}" + withEnv([ // Menetapkan variabel lingkungan untuk blok shell + "DOCKER_IMG=${DOCKER_IMAGE}", // Variabel untuk nama image Docker + "IMG_TAG=${IMAGE_TAG}" // Variabel untuk tag image ]) { - sh ''' + // Menjalankan perintah shell multi-baris + sh ''' # Hapus image yang tidak terpakai docker rmi $DOCKER_IMG:$IMG_TAG || true docker rmi $DOCKER_IMG:latest || true # Bersihkan builder cache untuk mencegah penumpukan storage - docker builder prune -f || true - ''' + docker builder prune -f || true + ''' // || true: lanjutkan meskipun ada error + // Menghapus image dengan tag versi dan latest + // Membersihkan cache builder Docker } - cleanWs() + cleanWs() // Membersihkan workspace Jenkins setelah build selesai } } } \ No newline at end of file