From 1085b42e54dfe7b64a37dfbe96287f0ca7bd0345 Mon Sep 17 00:00:00 2001 From: jose-rZM <100773386+jose-rZM@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:49:46 +0100 Subject: [PATCH 1/4] Improve backend docker image size --- backend/Dockerfile | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 81d0827..bd6c2c7 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,10 +1,22 @@ -FROM python:3.11-slim AS base +FROM python:3.11-slim AS builder +WORKDIR /build + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip wheel --no-cache-dir --no-deps -r requirements.txt -w /build/wheels + + +FROM python:3.11-slim +WORKDIR /app # ---- Build args (desde Jenkins) ---- ARG APP_VERSION=dev ARG GIT_COMMIT=local ARG COMMIT_AUTHOR=local -ARG BUILD_NUMBER=— +ARG BUILD_NUMBER=local # ---- Runtime env ---- ENV APP_VERSION=${APP_VERSION} \ @@ -21,18 +33,10 @@ LABEL app.version="${APP_VERSION}" \ git.author="${COMMIT_AUTHOR}" \ build.number="${BUILD_NUMBER}" - -WORKDIR /app - -RUN apt-get update && apt-get install -y --no-install-recommends \ - build-essential curl \ - && rm -rf /var/lib/apt/lists/* - -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt +COPY --from=builder /build/wheels /wheels +RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels COPY app ./app EXPOSE 8000 - CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] -- 2.34.1 From 741fc78b96b983b844bbb2caacfa2736079d85ba Mon Sep 17 00:00:00 2001 From: jose-rZM <100773386+jose-rZM@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:55:47 +0100 Subject: [PATCH 2/4] Update Jenkinsfile --- Jenkinsfile | 261 +++++++++++++++++++--------------------------------- 1 file changed, 95 insertions(+), 166 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index eda257a..5b3cb9e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,199 +1,128 @@ pipeline { - agent none + agent none - options { - timestamps() + options { + timestamps() + } + + environment { + CI = 'true' + NODE_OPTIONS = '--max_old_space_size=2048' + APP_VERSION = "1.0.${BUILD_NUMBER}" + DOCKER_BUILDKIT = '1' + } + + stages { + + stage('Init') { + agent any + 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() + } + } } - environment { - CI = 'true' - NODE_OPTIONS = '--max_old_space_size=2048' - - APP_VERSION = "1.0.${BUILD_NUMBER}" + stage('Backend: lint & test') { + 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 + ruff check app tests + pytest + ''' + } + } } - stages { - - stage('Init') { - agent any - 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 "Last commit: ${env.COMMIT_AUTHOR}" + stage('Frontend: check & build') { + agent { + docker { + image 'node:20-slim' } } - - - /* ========================= - BACKEND (Python) - ========================= */ - - stage('Backend: deps') { - agent { - docker { - image 'python:3.12' - args '-u root' - } - } - steps { - dir('backend') { - sh ''' - set -e - python --version - python -m venv .venv - . .venv/bin/activate - pip install --upgrade pip - pip install -r requirements-dev.txt - ''' - } + steps { + dir('frontend') { + sh ''' + set -e + npm install --no-progress --no-audit --prefer-offline + npm run check + npm run build + ''' } } + } - stage('Backend: lint & test') { - agent { - docker { - image 'python:3.12' - args '-u root' - } - } - steps { - dir('backend') { - sh ''' - set -e - . .venv/bin/activate - ruff check app tests - pytest - ''' - } - } - } - - /* ========================= - FRONTEND (Node) - ========================= */ - - stage('Frontend: deps') { - agent { - docker { - image 'node:20' - } - } - steps { - dir('frontend') { - sh ''' - set -e - node --version - npm --version - npm install --no-progress --no-audit --prefer-offline - ''' - } - } - } - - stage('Frontend: check & test') { - agent { - docker { - image 'node:20' - } - } - steps { - dir('frontend') { - sh ''' - set -e - npm run check - ''' - } - } - } - - stage('Frontend: build') { - agent { - docker { - image 'node:20' - } - } - steps { - dir('frontend') { - sh ''' - set -e - npm run build - ''' - } - } - } - - /* ========================= - DOCKER - ========================= */ - - stage('Docker: build images') { - when { - expression { - fileExists('backend/Dockerfile') && - fileExists('frontend/Dockerfile') - } - } + stage('Docker: build images') { agent any steps { sh ''' set -e - docker version 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} ./backend + -t cafeteria-backend:latest \ + -t cafeteria-backend:${BUILD_NUMBER} \ + ./backend docker build \ - -t cafeteria-frontend:${BUILD_NUMBER} ./frontend + -t cafeteria-frontend:latest \ + -t cafeteria-frontend:${BUILD_NUMBER} \ + ./frontend ''' } } - stage('Deploy frontend & backend') { - agent any - steps { - sh ''' - set -e + stage('Deploy frontend & backend') { + agent any + steps { + sh ''' + set -e - echo "Deploying build ${BUILD_NUMBER}" + docker rm -f cafeteria-backend cafeteria-frontend 2>/dev/null || true - docker rm -f cafeteria-backend cafeteria-frontend 2>/dev/null || true + docker run -d \ + --name cafeteria-backend \ + -p 8000:8000 \ + cafeteria-backend:latest - - docker run -d \ - --name cafeteria-backend \ - -p 8000:8000 \ - cafeteria-backend:${BUILD_NUMBER} - - docker run -d \ - --name cafeteria-frontend \ - -p 3000:80 \ - cafeteria-frontend:${BUILD_NUMBER} - ''' - } - } - } - - post { - always { - script { - node { - cleanWs() - } - } + docker run -d \ + --name cafeteria-frontend \ + -p 3000:80 \ + cafeteria-frontend:latest + ''' } } } + + post { + always { + sh ''' + docker image prune -a -f || true + docker builder prune -a -f || true + ''' + cleanWs() + } + } +} -- 2.34.1 From 5a61d32405b406110171123911c06915fed31c76 Mon Sep 17 00:00:00 2001 From: jose-rZM <100773386+jose-rZM@users.noreply.github.com> Date: Mon, 15 Dec 2025 17:44:20 +0100 Subject: [PATCH 3/4] Deploy only in main branch --- Jenkinsfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 5b3cb9e..d01c4cc 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -95,6 +95,10 @@ pipeline { } stage('Deploy frontend & backend') { + when { + branch 'main' + } + agent any steps { sh ''' -- 2.34.1 From 9271b558c3c1d535d74ff9a9e71264879c1ee535 Mon Sep 17 00:00:00 2001 From: jose-rZM <100773386+jose-rZM@users.noreply.github.com> Date: Mon, 15 Dec 2025 18:06:33 +0100 Subject: [PATCH 4/4] Rollback when failed deploy --- Jenkinsfile | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d01c4cc..71da272 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -104,17 +104,53 @@ pipeline { sh ''' set -e - docker rm -f cafeteria-backend cafeteria-frontend 2>/dev/null || true + : "${BACKEND_PORT:?Missing BACKEND_PORT}" + : "${BACKEND_HEALTH_URL:?Missing BACKEND_HEALTH_URL}" + : "${FRONTEND_PORT:?Missing FRONTEND_PORT}" + : "${FRONTEND_HEALTH_URL:?Missing FRONTEND_HEALTH_URL}" + + + # Backend + 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 8000:8000 \ + -p "$BACKEND_PORT":"$BACKEND_PORT" \ cafeteria-backend:latest + sleep 5 + + if ! curl -fs "$BACKEND_HEALTH_URL"; then + echo "Backend failed, rollback" + docker rm -f cafeteria-backend + docker run -d \ + --name cafeteria-backend \ + -p "$BACKEND_PORT":"$BACKEND_PORT" \ + cafeteria-backend:previous + exit 1 + fi + + # Frontend + 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 3000:80 \ + -p "$FRONTEND_PORT":80 \ cafeteria-frontend:latest + + sleep 3 + + if ! curl -fs "$FRONTEND_HEALTH_URL"; then + echo "Frontend failed, rollback" + docker rm -f cafeteria-frontend + docker run -d \ + --name cafeteria-frontend \ + -p "$FRONTEND_PORT":80 \ + cafeteria-frontend:previous + exit 1 + fi ''' } } -- 2.34.1