pipeline { agent none options { timestamps() } environment { NODE_OPTIONS = '--max_old_space_size=2048' APP_VERSION = "1.0.${BUILD_NUMBER}" DOCKER_BUILDKIT = '1' } stages { stage('Init') { agent any when { branch 'main' } steps { script { env.COMMIT_AUTHOR = sh( script: "git show -s --format=%an HEAD", returnStdout: true ).trim() env.COMMIT_SHORT = sh( script: "git rev-parse --short HEAD", returnStdout: true ).trim() } echo "CD deploy for main (${env.COMMIT_SHORT})" } } /* ========================= TESTS ========================= */ stage('Backend: test (main)') { when { branch 'main' } agent { docker { image 'python:3.11-slim' args '-u root' } } steps { dir('backend') { sh ''' set -e python -m venv .venv . .venv/bin/activate pip install --upgrade pip pip install -r requirements-dev.txt pytest ''' } } } stage('Frontend: build (main)') { when { branch 'main' } agent { docker { image 'node:20-slim' } } steps { dir('frontend') { sh ''' set -e npm install --no-progress --no-audit --prefer-offline npm run build ''' } } } /* ========================= DOCKER BUILD ========================= */ stage('Docker: build images') { when { branch 'main' } agent any steps { sh ''' set -e docker build \ --build-arg APP_VERSION=${APP_VERSION} \ --build-arg GIT_COMMIT=${COMMIT_SHORT} \ --build-arg COMMIT_AUTHOR="${COMMIT_AUTHOR}" \ --build-arg BUILD_NUMBER=${BUILD_NUMBER} \ -t cafeteria-backend:${BUILD_NUMBER} \ -t cafeteria-backend:latest \ ./backend docker build \ -t cafeteria-frontend:${BUILD_NUMBER} \ -t cafeteria-frontend:latest \ ./frontend ''' } } /* ========================= DEPLOY ========================= */ stage('Deploy frontend & backend') { when { branch 'main' } agent any environment { JENKINS_BASE_URL = 'http://jenkins:8080' JENKINS_JOB_NAME = 'Espetos' } steps { withCredentials([ usernamePassword( credentialsId: 'jenkins-api-token', usernameVariable: 'JENKINS_USER', passwordVariable: 'JENKINS_TOKEN' ) ]) { sh ''' set -e : "${BACKEND_PORT:?Missing BACKEND_PORT}" : "${BACKEND_HEALTH_URL:?Missing BACKEND_HEALTH_URL}" : "${FRONTEND_PORT:?Missing FRONTEND_PORT}" : "${FRONTEND_HEALTH_URL:?Missing FRONTEND_HEALTH_URL}" echo "Deploying backend ${BUILD_NUMBER}" docker tag cafeteria-backend:latest cafeteria-backend:previous || true docker rm -f cafeteria-backend 2>/dev/null || true docker run -d \ --name cafeteria-backend \ -p "$BACKEND_PORT":"$BACKEND_PORT" \ cafeteria-backend:latest sleep 5 if ! curl -fs "$BACKEND_HEALTH_URL"; then echo "Backend failed, rolling back" docker rm -f cafeteria-backend docker run -d \ --name cafeteria-backend \ -p "$BACKEND_PORT":"$BACKEND_PORT" \ cafeteria-backend:previous exit 1 fi echo "Deploying frontend ${BUILD_NUMBER}" docker tag cafeteria-frontend:latest cafeteria-frontend:previous || true docker rm -f cafeteria-frontend 2>/dev/null || true docker run -d \ --name cafeteria-frontend \ -p "$FRONTEND_PORT":80 \ cafeteria-frontend:latest sleep 3 if ! curl -fs "$FRONTEND_HEALTH_URL"; then echo "Frontend failed, rolling back" docker rm -f cafeteria-frontend docker run -d \ --name cafeteria-frontend \ -p "$FRONTEND_PORT":80 \ cafeteria-frontend:previous exit 1 fi ''' } } } } post { always { cleanWs() } } }