서론
이 장은 Jenkins에서 CI/CD를 하기 위한 아래 이미지에 표기된 Webhook 방법에 대해서 설명한다.
GitHub에서 연결하는 방법과 Git에서 연결하는 방법 두 가지 설명한다.
GitHub 이벤트와 Jenkins 연결

동작방식
- Dev PC와 Jenkins는 동일한 PC에서 진행했다.
- 이때 Jenkins는 MacOS를 사용하며 Service PC는 Linux를 사용한다.
- 따라서 Jenkins가 MacOS 에서 진행됐다는 점을 참고한다.

1. Jenkins DNS 설정
WebHook을 설정하기 위해 먼저 Jenkins를 외부에서 접속이 가능해야 한다. 즉 DNS설정을 해줘야한다. 나는 Ngrok을 사용했다.

2. Jenkins 플러그인 설치
GitHUB관련 기능을 사용하기 위한 플러그인들을 설치해두자.

Dashboard → Manage jenkins → Plugins에서 Avaliable plugins 탭의 검색부분에 ssh agent 플러그인을 설치한다.

플러그인 설치 후 Jenkins를 재시작한다.
brew services restart jenkins-lts
3. SSH 키 생성
- Jenkins가 Service PC에 접속가능하도록 하기 위해 공개-비공개 키를 생성해 등록해주자.
- 먼저 클라이언트(Jenkins가 설치된 환경)에서 공개, 비공개 키를 생성하고, 서버(Service PC)에 공개키를 등록한다.
- 이를 통해 클라이언트가 서버에 접속할 때 비밀번호 없이 접속이 가능하다.
아래 명령어를 통해 키를 새성한다.
ssh-keygen -t rsa
아래 명령어를 통해 키를 등록해주자 groupai@XXX.XXX.XX.XXX ⇒ 서버주소
ssh-copy-id -i ~/.ssh/id_rsa.pub -p {port_number} g__ai@21X.XXX.XX.216
5. Creating SSH Credentials
Dashboard → Manage Jenkins → Credentials에서 global 버튼을 누르고 Add Credentials를 클릭한다.


- 이때 클라이언트의(Jenkins Server) 개인키 내용을 입력해줘야하는데
-----BEGIN RSA PRIVATE KEY---—와-----END RSA PRIVATE KEY---—도 같이 입력해줘야한다.(헤더와 푸터)
cat ~/.ssh/id_rsa

- 작성을 완료하면 아래처럼 생긴다.

6. Creating Github Access Token
깃허브 토큰 접속 토큰 생성하기



이렇게하면 토큰이 생성된다. 토큰이 생성된 화면은 벗어나면 다시 토큰을 찾을 방법이 없음으로 다시 생성해야 한다. 그러니 메모장 같은 곳에 잘 메모해두자.

7. Setting WebHook
WebHook을 설정하고자 하는 레포지토리를 클릭 후 Settings → Webhooks 클릭


- Payload URL: Ngrok에서 생성한
Jenkins 서버주소에/github-webhook/를 추가해준다. - Content type:
application/json으로 변경한다. - Add webhook 버튼을 클릭한다

8. Creating Git Hub Credentials

Credentials 등록 화면 이동
- (global) 링크를 눌러
Credentials등록 화면으로 이동한다.

- Add Credentials를 클릭한다.

- Credential을 아래와 같이 작성한다.


https://junhyunny.github.io/information/jenkins/github/jenkins-github-webhook/
9. Making Pipeline



Quiet Period는 특정시간동안 많은 빌드 요청이 온 경우 가장 최신 커밋만을 빌드하도록하는 스케쥴링 같은 옵션이다. 웹 훅이랑은 상관이없는 부분이니 신경쓰지말자.

그리고 아래처럼 파이프라인을 설정한다.
Credential에는 8. Creating Git Hub Credentials 에서 생성했던 인증서를 넣어주면 된다.


여기까지 했다면 기본적인 준비는 끝이 났다.
Pipeline script from SCM 으로 설정한 경우 내가 위에서 설정한 https://github.com/woorej/test_jenkins2.git 이 레포지토리에 Jenkinsfile 이름으로 가진 파일이 있어야한다. 파이프라인 설정에서 Script path경로에 있는 Jenkinsfile을 찾기 때문이다.
아래 깃 리포지토리를 보면 Jenkins파일이 있는 것을 확인할 수 있다.

스크립트 코드는 아래 공유한다. 테스트 용도라 대충 만든 것이니 흐름과 문법 이해하도록..
- Checkout: jenkins가 github에 있는 pipline이 적혀진 Jenkinsfile을 요청한다. 따라서 checkout 스테이지를 추가한다.
- Prepare Environment: 서버에 접속해 디렉토리가 있으면 프로젝트가 있다고 간주하고 pull만하며 없는 경우에만 clone을한다.
- Setup Virtual Environment: 파이썬 가상환경을 만들고 필요한 라이브러리들을 설치한다.
- Run Tests: Unit Test 코드를 실행한다.
- Build and Deploy: 스테이지 이름이 Deploy가 적혀있는데 나는 빌드까지만 하고 끝냈다.
아래 파이프라인이 깃허브 프로젝트의 최상위 디렉토리에 Jenkinsfile로 있으면 젠킨스가 파이프라인을 읽어서 순차적으로 실행하는 원리이다.

pipeline {
agent any
environment {
SERVER_IP = "218.xxx.xx.xxx"
SERVER_PORT = "xxxxx"
SERVER_USER = "groupai"
REPO_URL = 'https://github.com/woorej/test_jenkins2.git'
PROJECT_DIR = '/home/groupai/workspace/test_jenkins2'
}
stages {
stage('Checkout') {
steps {
checkout scm: [ $class: 'GitSCM', branches: [[name: '*/master']],
userRemoteConfigs: [[credentialsId: 'github-access-token',
url: 'https://github.com/woorej/test_jenkins2.git']]]
}
}
stage('Prepare Environment') {
steps {
script {
sshagent(['server_7']) {
sh """
ssh -o StrictHostKeyChecking=no -p ${SERVER_PORT} ${SERVER_USER}@${SERVER_IP} '
if [ ! -d "${PROJECT_DIR}" ]; then
echo "Directory does not exist. Cloning repository..."
git clone ${REPO_URL} ${PROJECT_DIR}
else
echo "Directory exists. Pulling latest changes..."
cd ${PROJECT_DIR}
git pull
fi
'
"""
}
}
}
}
stage('Setup Virtual Environment') {
steps {
script {
sshagent(['server_7']) {
sh """
ssh -o StrictHostKeyChecking=no -p ${SERVER_PORT} ${SERVER_USER}@${SERVER_IP} '
if [ ! -d "${PROJECT_DIR}/venv" ]; then
echo "Creating virtual environment..."
/usr/bin/python3.11 -m venv ${PROJECT_DIR}/venv
fi
source ${PROJECT_DIR}/venv/bin/activate
pip3 install -r ${PROJECT_DIR}/requirements.txt
deactivate
'
"""
}
}
}
}
stage('Run Tests') {
steps {
script {
sshagent(['server_7']) {
sh """
ssh -o StrictHostKeyChecking=no -p ${SERVER_PORT} ${SERVER_USER}@${SERVER_IP} '
source ${PROJECT_DIR}/venv/bin/activate
python3 -m pytest ${PROJECT_DIR}/tests
deactivate
'
"""
}
}
}
}
stage('Build and Deploy') {
steps {
script {
sshagent(['server_7']) {
sh """
ssh -o StrictHostKeyChecking=no -p ${SERVER_PORT} ${SERVER_USER}@${SERVER_IP} '
source ${PROJECT_DIR}/venv/bin/activate
pip3 freeze | grep calculator && pip3 uninstall -y calculator
pip3 install ${PROJECT_DIR}
'
"""
}
}
}
}
}
}
https://github.com/woorej/test_jenkins2/tree/master
10. Test 동작확인
성공적으로 동작한다.

https://1minute-before6pm.tistory.com/52
Git 이벤트와 Jenkins 연결
아래 3대의 서버는 전부 다 같은 네트워크 망에 있다고 가정하고 설명한다. 또한 운영체제는 전부 우분투 리눅스가 설치되어 있다.
- Git Server (# 22)
- Service Server (# 8)
- Jenkins Server (# 6)
이번 챕터에서는Jenkins가 Linux Ubuntu에서 진행되었음을 참고하자.
- 깃허브는 Access Token이라는 것을 발급받아서 동작했었다.
- 그러나 Hub가 아닌 사내에서 사용하는 git은 결국 ssh key로 인증을 받고 통신한다.
동작 방식

1. Jenkins 플러그인 설치
설명한 플러그인들이 설치되어야 한다: 2. Jenkins 플러그인 설치
2. SSH키 & Jenkins Credentials 생성하기
위 동작방식에서는 3개의 키가 필요하다.
- Jenkins(6번) 서버 -> 깃 서버(22번) 서버로의 바로 접속이 가능해야 깃서버에서 Jenkinsfile을 가져와 실행이 가능하다.
- 따라서 6번의 공개키를 등록해야한다.
- Jenkins(6번) 서버 -> Service PC(8번) 서버로의 접속이 가능해야 Service PC에서 프로젝트를 빌드하고 실행할 수 있다.
- 따라서 6번의 공개키를 등록해야한다.
- Service PC(8번)에서 깃 서버(22번) 8번서버에서 git clone을 해야한다.
- 따라서 8번의 공개키가 필요하다.
- 그러나 Jenkins의 Credential에 등록할 필요는 없다. 왜냐하면 Jenkins가 8번에 ssh로 접속해 스크립트를 실행하는 것이기 때문이다.
- 주체가 Jenkins가 클라이언트가 아닌 8번이 클라이언트로 22번에 접근하기 때문이다
2-1. 1번 키 등록
깃 서버의 공개키를 Jenkins서버에 등록
먼저 6번서버에서 깃 서버의 SSH키를 3. SSH 키 생성 이 방식으로 했더니 아래와 같은 에러가 생긴다.
RSA의 형식이 아닌 ED25519 포멧을 원하는것 처럼 보인다..
이는 젠킨스 서버(6번)에서 깃서버(22번)SSH Key를 읽지 못해서 생기는 문제이다.
Failed to connect to repository : Command "git ls-remote -h -- ssh://git@192.168.1.22/git/temp/hello_world.git HEAD" returned status code 128:
stdout:
stderr: No ED25519 host key is known for 192.168.1.22 and you have requested strict checking.
Host key verification failed.
fatal: Could not read from remote repository.

참고로 ssh 접속관련 로그는 아래 명령어를 통해 확인가능하다.
sudo tail -f /var/log/auth.log
- 해결 방법은 깃 서버(22번)에 접속해서 ED25519 형식의 키값을 생성하여 해당 공개키를 Jenkins서버의
/var/lib/jenkins/.ssh/known_hosts위치에 등록해줘야 하면 된다. - Git서버(22번)에 접속해서 아래 명령어를 타이핑한다. 이메일주소는 그냥 대충입력해도 상관없다.
ssh-keygen -t ed25519 -C "your_email@example.com"
- 계속 엔터를 눌러서 passphrase 없이 설정가능하도록 해주자.
192.168.1.22의ed25519유형의 SSH 공개 키를 출력하라는 의미이다. 출력값을 복사해두자.
ssh-keyscan -t ed25519 192.168.1.22
- Jenkins 서버에 접속한다.
/var/lib/jenkins/.ssh/known_hosts위치에 복사한 값을 붙여넣기 해준다. - 나의 경우 .ssh 디렉토리가 없어 mkdir로 생성해주었고 known_hosts파일 또한 vim으로 열어 붙여넣기해주었다.
Jenkins서버의 공개키를 깃 서버에 등록
- Jenkins 서버에서 ED25519 키를 생성한다.
ssh-keygen -t ed25519 -C "your_email@example.com"
- 생성된 공개키는 깃서버(22번)에 등록해준다.
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p {port_number} git@XXX.XXX.XX.22
Jenkins의 Credential 등록
나는 아래처럼 생성했다.

Jenkins서버에서 아래 명령어를 입력해 나온 출력값을 저 add를 눌러 넣어준다.
cat ~/.ssh/id_ed25519
Head: -----BEGIN OPENSSH PRIVATE KEY---—
Footer: -----END OPENSSH PRIVATE KEY---—
까지 전부 포함되어있어야 한다.

그리고 저장해준다.
그럼 이제 6번서버에서 22번 Jenkins 서버로 에러없이 접속이 가능하다는 것을 알 수 있다.

2-2. 2번 키 등록
이 경우는 그냥 rsa타입의 ssh를 키를 등록해주면된다.
3. SSH 키 생성 마찬가지로 Jenkins Server(# 6)에서 키를 만들어 공개키를 Service PC(# 8)에 전송하면 된다.
Jenkins의 Credential 등록
Key는 마찬가지로 Jenkins Server(# 6)의 개인키를 넣어주면 된다.

2-3. 3번 키 등록
마찬가지로 rsa타입의 ssh를 키를 등록해주면된다.
3. SSH 키 생성 마찬가지로 Service PC(# 8)에서 키를 만들어 공개키를 깃 서버(# 22)에 전송하면 된다.
다만 Jenkins가 직접적으로 컨트롤하는 영역이 아니기 때문에 Jenkins의 Credential에 등록해 줄 필요는 없다.
3. Git Webhook
깃을 사용시 커밋 또는 푸쉬가 발생한 경우 이때 같이 어떤 스크립트가 실행되면서 이벤트를 발생하는 원리이다.
훅은 클라이언트단에서 훅이 있고 깃 서버단에서의 훅이 있다.
프로세스
깃 서버에서의 훅
- 깃 서버에서 후킹을 발생하도록 하는 경우 푸쉬가 들어온 후에 스크립트가 실행되는 원리

클라이언트에서의 훅
- 클라이언트에서는 push가 일어난 후에 훅이 발생하는
post-push와 같은 기능은 없다. - 대신
pre-push훅이 존재하는데git push명령을 실행하면 동작하지만 리모트(깃서버)로 데이터를 전송하기 전에 동작한다.

서버에서의 훅과 클라이언트 훅 둘중 하나를 선택해서 설정해주면 된다.
깃 서버에서의 훅
- git 서버(# 22)에 접속하고 후킹을 하고싶은 레포지토리에 들어간다.
- 나는 hello_world.git 이라는 레포지토리를 만들었다.
- 그리고 해당. 레포지토리에 hooks라는 폴더에 들어가보면 아래 처럼
.sample이라는 확장자를 가진 파일들이 나열되어 있다. post-receive.sample이라는 파일을 수정해야한다.- 만약 없다면 post-receive 파일을 생성해준다(이 때 실행가능한 권한을 부여해야한다.)
chmod +x post-receive .sample은 샘플파일들로 실행되지 않는다. 따라서 동작을 원한다면.sample이라는 확장자를 제거해주면 된다

https://www.git-scm.com/book/ko/v2/Git맞춤-Git-Hooks
- post-receive를 열어보면 아래 처럼 작성해준다.
vim post-receive
Script
git@rev-devel:~/git/hmlf/ai_client.git/hooks$ cat post-receive
JENKINS_URL="https://33e1-218-233-16-216.ngrok-free.app"
JENKINS_JOB="ai_client"
JENKINS_USER="admin"
JENKINS_TOKEN="<my_token>"
TRIGGER_URL="${JENKINS_URL}/job/${JENKINS_JOB}/build?token=${JENKINS_TOKEN}"
LOG_FILE="/tmp/post-receive.log"
{
echo "[$(date)] Triggering Jenkins Job"
echo "Jenkins URL: ${TRIGGER_URL}"
RESPONSE=$(curl -X POST "${TRIGGER_URL}" --user "${JENKINS_USER}:${JENKINS_TOKEN}" -w "%{http_code}")
echo "Jenkins Response: ${RESPONSE}"
} >> "${LOG_FILE}" 2>&1- JENKINS_URL: git Server에서 Jenkins에 접속가능한 주소
- JENKINS_JOB: Pipeline 생성시 사용했던 이름

- JENKINS_USER: 사용자 계정입력
- JENKINS_TOKEN: Jenkins에서 토큰 발급(WebUI 접속필요) 여기서 생만들어 뒀던 토큰 값 입력
- 그 후 아래는 그냥 복사 해주면 된다. 다만 LOG_FILE 디렉토리에 파일을 미리 만들어두자.
클라이언트에서의 훅
클라이언트에서의 훅은 깃 서버(# 22)번이 아닌 클라이언트 환경에서 해야한다.
먼저 git clone을 통해 설정하고자 하는 레포지토리를 내려받고 프로젝트 디렉토리로 이동해 .git으로 들어간다.
그리고 pre-push를 수정해준다. 스크립트 내용은 동일하니 생략하겠다.
cd hello_world/.git/hooks
cp pre-push.sample pre-push
chmod +x pre-push
vim pre-push
4. Making Pipeline





저장을 눌러주고 ai_client.git에 푸쉬를 한다. 그리고 트리거가 발생하는지 확인하면된다.
또는 수동으로하고싶다면 아래 명령어를 실행한다.
curl -X POST "https://33e1-218-233-16-216.ngrok-free.app/job/ai_client/build?token={JENKINS_TOKEN}" --user "admin:<JENKINS_TOKEN>" -w "%{http_code}"
이 명령어는 Jenkins Job을 수동으로 트리거하며, 응답 코드를 출력한다.
응답 코드가 200 또는 201이면 성공적으로 트리거된 것이다.
스크립트
Script
def unit_test_failed = false
pipeline {
agent any
environment {
SERVER_IP = "192.168.x.x"
REPORT_IP = "http://192.168.x.x:8080"
SERVER_USER = "test"
REPO_URL = 'ssh://git@192.168.x.x/git/ai_core_service.git'
ENGINE_REPO_URL = 'ssh://git@192.168.x.x/ai_engine.git'
TRAIN_REPO_URL = 'ssh://git@192.168.x.x/ai_train_engine.git'
COMMON_LIB_REPO = 'ssh://git@192.168.x.x/ai_fw_utility.git'
AUTHENTICATION_REPO = 'ssh://git@192.168.x.x/ai_sw.git'
STARTKIT_REPO = 'ssh://git@192.168.x.x/ai_starterkit.git'
CUSTOM_BUILD_URL = "${REPORT_IP}/job/${env.JOB_NAME}/${env.BUILD_NUMBER}/"
}
stages {
stage('Notify Build Start') {
steps {
script {
slackSend (channel: 'C078T1XJE57', color: 'good', message: "Build STARTED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})")
}
}
}
stage('Prepare Environment') {
steps {
script {
sshagent(['jenkins_to_service_key']) {
sh """
ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} '
set -e
HOME_DIR=\$(echo ~${SERVER_USER})
WORKSPACE_DIR="\${HOME_DIR}/workspace"
REPO_NAME=\$(basename -s .git ${REPO_URL})
VENV_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}_venv"
if [ ! -d "\${WORKSPACE_DIR}" ]; then
echo "Directory does not exist. Creating workspace directory..."
mkdir -p \${WORKSPACE_DIR}
echo "\${WORKSPACE_DIR} successfully created"
fi
if [ ! -d "\${VENV_DIR}" ]; then
echo "Creating virtual environment..."
/usr/bin/python3.11 -m venv --system-site-packages \${VENV_DIR}
fi
'
"""
}
}
}
post {
failure {
script {
slackSend (channel: 'C078T1XJE57', color: 'danger', message: "Build FAILED in stage '${env.STAGE_NAME}': Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})")
}
}
}
}
stage('Pip install Project') {
steps {
script {
sshagent(['jenkins_to_service_key']) {
sh """
ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} '
set -e
HOME_DIR=\$(echo ~${SERVER_USER})
WORKSPACE_DIR="\${HOME_DIR}/workspace"
REPO_NAME=\$(basename -s .git ${REPO_URL})
PROJECT_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}"
VENV_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}_venv"
source \${VENV_DIR}/bin/activate
pip3 install setuptools wheel
if [ ! -d "\${PROJECT_DIR}" ]; then
echo "Project directory does not exist. Cloning repository..."
cd \${WORKSPACE_DIR}
git clone ${REPO_URL}
cd \${PROJECT_DIR}
pip3 install ./
else
echo "Project directory exists. Pulling latest changes..."
cd \${PROJECT_DIR}
git pull
pip3 uninstall \${REPO_NAME} -y || true
pip3 install ./
fi
deactivate
'
"""
}
}
}
post {
failure {
script {
slackSend (channel: 'C078T1XJE57', color: 'danger', message: "Build FAILED in stage '${env.STAGE_NAME}': Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})")
}
}
}
}
stage('Pip install Dependency') {
steps {
script {
sshagent(['jenkins_to_service_key']) {
sh """
ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} '
set -e
HOME_DIR=\$(echo ~${SERVER_USER})
WORKSPACE_DIR="\${HOME_DIR}/workspace"
REPO_NAME=\$(basename -s .git ${REPO_URL})
VENV_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}_venv"
source \${VENV_DIR}/bin/activate
pip3 install torch torchvision torchaudio
for REPO_URL in ${COMMON_LIB_REPO} ${ENGINE_REPO_URL} ${TRAIN_REPO_URL}; do
REPO_NAME=\$(basename -s .git \${REPO_URL})
REPO_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}"
if [ ! -d "\${REPO_DIR}" ]; then
echo "Cloning repository \${REPO_NAME}..."
cd \${WORKSPACE_DIR}
git clone \${REPO_URL}
else
echo "Repository \${REPO_NAME} exists. Pulling latest changes..."
cd \${REPO_DIR}
git pull
pip3 uninstall \${REPO_NAME} -y || true
fi
cd \${REPO_DIR}
pip3 install ./
done
pip3 install -U Pillow
deactivate
'
"""
}
}
}
post {
failure {
script {
slackSend (channel: 'C078T1XJE57', color: 'danger', message: "Build FAILED in stage '${env.STAGE_NAME}': Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})")
}
}
}
}
stage('Pip install Authentication') {
steps {
script {
sshagent(['jenkins_to_service_key']) {
sh """
ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} '
set -e
HOME_DIR=\$(echo ~${SERVER_USER})
WORKSPACE_DIR="\${HOME_DIR}/workspace"
REPO_NAME=\$(basename -s .git ${REPO_URL})
VENV_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}_venv"
source \${VENV_DIR}/bin/activate
for REPO_URL in ${AUTHENTICATION_REPO}; do
REPO_NAME=\$(basename -s .git \${REPO_URL})
REPO_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}"
if [ ! -d "\${REPO_DIR}" ]; then
echo "Cloning repository \${REPO_NAME}..."
cd \${WORKSPACE_DIR}
git clone \${REPO_URL}
else
echo "Repository \${REPO_NAME} exists. Pulling latest changes..."
cd \${REPO_DIR}
git pull
pip3 uninstall \${REPO_NAME} -y || true
fi
cd \${REPO_DIR}
pip3 install ./
done
deactivate
'
"""
}
}
}
post {
failure {
script {
slackSend (channel: 'C078T1XJE57', color: 'danger', message: "Build FAILED in stage '${env.STAGE_NAME}': Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})")
}
}
}
}
stage('Unit Test') {
steps {
script {
sshagent(['jenkins_to_service_key']) {
def result = sh(
script: """
ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} '
set -e
HOME_DIR=\$(echo ~${SERVER_USER})
WORKSPACE_DIR="\${HOME_DIR}/workspace"
REPO_NAME=\$(basename -s .git ${REPO_URL})
VENV_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}_venv"
PROJECT_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}"
source \${VENV_DIR}/bin/activate
cd \${PROJECT_DIR}
pip3 install pytest
pip3 install pytest-mock
pip3 install pytest-html
pytest --html=\${REPO_NAME}_report.html --self-contained-html
RESULT=\$?
if [ \${RESULT} -ne 0 ]; then
echo "Error: Unit test failed"
# exit 1
fi
deactivate
exit \${RESULT}
'
""", returnStatus: true
)
if (result != 0) {
unit_test_failed = true
}
}
}
}
post {
always {
sshagent(['jenkins_to_service_key']) {
script {
def HOME_DIR = sh(script: "ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} 'echo ~${SERVER_USER}'", returnStdout: true).trim()
def WORKSPACE_DIR = "${HOME_DIR}/workspace"
def REPO_NAME = sh(script: "basename -s .git ${REPO_URL}", returnStdout: true).trim()
def REMOTE_FILE_PATH = "${WORKSPACE_DIR}/${REPO_NAME}/${REPO_NAME}_report.html"
def LOCAL_FILE_PATH = "${env.WORKSPACE}/${REPO_NAME}_report.html"
echo "HOME_DIR: ${HOME_DIR}"
echo "WORKSPACE_DIR: ${WORKSPACE_DIR}"
echo "REPO_NAME: ${REPO_NAME}"
echo "REMOTE_FILE_PATH: ${REMOTE_FILE_PATH}"
echo "LOCAL_FILE_PATH: ${LOCAL_FILE_PATH}"
sh "scp -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP}:${REMOTE_FILE_PATH} ${LOCAL_FILE_PATH}"
slackUploadFile (
channel: 'C078T1XJE57',
filePath: "${REPO_NAME}_report.html",
initialComment: "Unit Test Report for Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})"
)
if (unit_test_failed) {
slackSend (
channel: 'C078T1XJE57',
attachments: [
[
color: "warning",
text: "Unit Test Failed for Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'. Please check the report for details."
]
]
)
} else {
slackSend (
channel: 'C078T1XJE57',
attachments: [
[
color: "success",
text: "Unit Test Succeeded for Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'. Please check the report for details."
]
]
)
}
}
}
}
}
}
stage('Clone StartKit') {
steps {
script {
sshagent(['jenkins_to_service_key']) {
sh """
ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} '
set -e
HOME_DIR=\$(echo ~${SERVER_USER})
WORKSPACE_DIR="\${HOME_DIR}/workspace"
STARTERKIT_REPO_NAME=\$(basename -s .git ${STARTKIT_REPO})
STARTERKIT_DIR="\${WORKSPACE_DIR}/\${STARTERKIT_REPO_NAME}"
if [ ! -d "\${STARTERKIT_DIR}" ]; then
echo "Starter Kit does not exist. Cloning repository ..."
cd \${WORKSPACE_DIR}
git clone ${STARTKIT_REPO}
else
echo "Starter Kit exists. Pulling latest changes..."
cd \${STARTERKIT_DIR}
git pull
fi
'
"""
}
}
}
post {
failure {
script {
slackSend (channel: 'C078T1XJE57', color: 'danger', message: "Build FAILED in stage '${env.STAGE_NAME}': Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})")
}
}
}
}
stage('Install Nuitka') {
steps {
script {
sshagent(['jenkins_to_service_key']) {
sh """
ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} '
set -e
HOME_DIR=\$(echo ~${SERVER_USER})
WORKSPACE_DIR="\${HOME_DIR}/workspace"
REPO_NAME=\$(basename -s .git ${REPO_URL})
VENV_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}_venv"
source \${VENV_DIR}/bin/activate
pip3 install nuitka==2.3.2
deactivate
'
"""
}
}
}
post {
failure {
script {
slackSend (channel: 'C078T1XJE57', color: 'danger', message: "Build FAILED in stage '${env.STAGE_NAME}': Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})")
}
}
}
}
stage('Run Nuitka Script') {
steps {
script {
sshagent(['jenkins_to_service_key']) {
sh """
ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} '
set -e
HOME_DIR=\$(echo ~${SERVER_USER})
WORKSPACE_DIR="\${HOME_DIR}/workspace"
REPO_NAME=\$(basename -s .git ${REPO_URL})
VENV_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}_venv"
PROJECT_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}"
STARTERKIT_REPO_NAME=\$(basename -s .git ${STARTKIT_REPO})
source \${VENV_DIR}/bin/activate
NUITKA_SCRIPT="\${WORKSPACE_DIR}/\${STARTERKIT_REPO_NAME}/packaging/nuitka/\${REPO_NAME}.sh"
cd \${PROJECT_DIR}
bash \${NUITKA_SCRIPT}
echo "\${REPO_NAME} Packing Finished!"
deactivate
'
"""
}
}
}
post {
failure {
script {
slackSend (channel: 'C078T1XJE57', color: 'danger', message: "Build FAILED in stage '${env.STAGE_NAME}': Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})")
}
}
}
}
stage('Test Nuitka') {
steps {
script {
sshagent(['jenkins_to_service_key']) {
sh """
ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} '
set -e
HOME_DIR=\$(echo ~${SERVER_USER})
WORKSPACE_DIR="\${HOME_DIR}/workspace"
REPO_NAME=\$(basename -s .git ${REPO_URL})
PROJECT_DIR="\${WORKSPACE_DIR}/\${REPO_NAME}"
DST_DIR=\$(find \${PROJECT_DIR} -type d -name "*.dist")
BIN_FILE=\$(find \${DST_DIR} -type f -name "*.bin")
if [ -z "\${DST_DIR}" ] || [ -z "\${BIN_FILE}" ]; then
echo "Error: .dist directory or .bin file not found"
exit 1
fi
\${BIN_FILE} -help
RESULT=\$?
if [ \${RESULT} -ne 0 ]; then
echo "Error: .bin file execution failed"
exit 1
fi
echo "Nuitka script executed successfully"
'
"""
}
}
}
post {
failure {
script {
slackSend (channel: 'C078T1XJE57', color: 'danger', message: "Build FAILED in stage '${env.STAGE_NAME}': Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})")
}
}
}
}
stage('Notify Build Result') {
steps {
script {
slackSend (channel: 'C078T1XJE57', color: 'good', message: "Build Finished: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${CUSTOM_BUILD_URL})")
}
}
}
}
}
Comment