From b784ea1cd536bb0ba2309816677c8467c6189103 Mon Sep 17 00:00:00 2001 From: AlexeerCT Date: Thu, 2 Apr 2026 11:18:18 -0500 Subject: [PATCH 1/4] Refactor Jenkinsfile to enhance build and deployment processes. Updated agent configuration to use a specific Docker label, improved database readiness checks with error handling, and implemented retry logic for Docker builds and pushes. Additionally, refined SSH key handling and ensured proper tagging for Docker images during the build process. --- Jenkinsfile | 76 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 6512be28..8a5be402 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,7 @@ pipeline { - agent any + agent { + label 'docker' + } options { timestamps() disableConcurrentBuilds() @@ -57,7 +59,6 @@ pipeline { DB_NAME="anexo76_test" DB_USER="anexo76" DB_PASS="anexo76" - DB_PORT="55432" export TEST_DATABASE_URL="postgresql://${DB_USER}:${DB_PASS}@${DB_CONTAINER}:5432/${DB_NAME}" PY_CONTAINER="anexo76-test-py-${BUILD_NUMBER}" TEST_NETWORK="anexo76-test-net-${BUILD_NUMBER}" @@ -80,13 +81,19 @@ pipeline { -e POSTGRES_PASSWORD="$DB_PASS" \ postgres:16-alpine - # Espera a que Postgres acepte conexiones + # Espera a que Postgres acepte conexiones (hasta ~60s) + READY=0 for i in $(seq 1 30); do if docker exec "$DB_CONTAINER" pg_isready -U "$DB_USER" -d "$DB_NAME" >/dev/null 2>&1; then + READY=1 break fi sleep 2 done + if [ "$READY" != "1" ]; then + echo "ERROR: Postgres no quedó listo a tiempo (30 intentos × 2s)." + exit 1 + fi docker exec "$DB_CONTAINER" pg_isready -U "$DB_USER" -d "$DB_NAME" docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -v ON_ERROR_STOP=1 -c " CREATE SCHEMA IF NOT EXISTS core; @@ -165,33 +172,47 @@ pipeline { stage('Build + push backend') { steps { - sh ''' - set -euo pipefail - docker build \ - --build-arg APP_VERSION="${APP_VERSION}" \ - -t "${REGISTRY}/${IMAGE_NAMESPACE}/backend:latest" \ - -f ./backend/Dockerfile \ - ./backend + script { + retry(3) { + sh ''' + set -euo pipefail + export DOCKER_BUILDKIT=1 + docker build \ + --build-arg APP_VERSION="${APP_VERSION}" \ + -t "${REGISTRY}/${IMAGE_NAMESPACE}/backend:${APP_VERSION}" \ + -t "${REGISTRY}/${IMAGE_NAMESPACE}/backend:latest" \ + -f ./backend/Dockerfile \ + ./backend - docker push "${REGISTRY}/${IMAGE_NAMESPACE}/backend:latest" - ''' + docker push "${REGISTRY}/${IMAGE_NAMESPACE}/backend:${APP_VERSION}" + docker push "${REGISTRY}/${IMAGE_NAMESPACE}/backend:latest" + ''' + } + } } } stage('Build + push frontend') { steps { - sh ''' - set -euo pipefail - docker build \ - --build-arg VITE_API_URL=https://anexo76-dev.aduanasoft.com/api/ \ - --build-arg VITE_KEYCLOAK_URL=https://anexo76-dev.aduanasoft.com/kcauth/ \ - --build-arg INTERNAL_API_URL=http://backend:3467/api/ \ - -t "${REGISTRY}/${IMAGE_NAMESPACE}/frontend:latest" \ - -f ./frontend/Dockerfile.prod \ - ./frontend + script { + retry(3) { + sh ''' + set -euo pipefail + export DOCKER_BUILDKIT=1 + docker build \ + --build-arg VITE_API_URL=https://anexo76-dev.aduanasoft.com/api/ \ + --build-arg VITE_KEYCLOAK_URL=https://anexo76-dev.aduanasoft.com/kcauth/ \ + --build-arg INTERNAL_API_URL=http://backend:3467/api/ \ + -t "${REGISTRY}/${IMAGE_NAMESPACE}/frontend:${APP_VERSION}" \ + -t "${REGISTRY}/${IMAGE_NAMESPACE}/frontend:latest" \ + -f ./frontend/Dockerfile.prod \ + ./frontend - docker push "${REGISTRY}/${IMAGE_NAMESPACE}/frontend:latest" - ''' + docker push "${REGISTRY}/${IMAGE_NAMESPACE}/frontend:${APP_VERSION}" + docker push "${REGISTRY}/${IMAGE_NAMESPACE}/frontend:latest" + ''' + } + } } } @@ -229,13 +250,19 @@ pipeline { ) ]) { script { + sh """ + set -euo pipefail + mkdir -p ~/.ssh + chmod 700 ~/.ssh + ssh-keyscan -T 15 -H '${env.DEV_SERVER_HOST}' >> ~/.ssh/known_hosts + """ def remote = [ name: 'dev-server', host: env.DEV_SERVER_HOST, user: env.DEV_SERVER_USER ?: env.DEV_SERVER_SSH_USER, identityFile: env.DEV_SERVER_KEY, passphrase: env.DEV_SERVER_KEY_PASSPHRASE, - allowAnyHosts: true + allowAnyHosts: false ] sshCommand remote: remote, command: ''' set -euo pipefail @@ -243,6 +270,7 @@ pipeline { # Asume docker login ya configurado en el host destino para evitar exponer secretos por interpolación. docker compose -f docker-compose.prod.yml pull docker compose -f docker-compose.prod.yml up -d + # Solo imágenes colgantes (sin tag); no borra imágenes en uso por otros contenedores. docker image prune -f ''' } From 5c18108e792cc7daf510e7a2b6eeff6f594dcc62 Mon Sep 17 00:00:00 2001 From: AlexeerCT Date: Thu, 2 Apr 2026 11:20:56 -0500 Subject: [PATCH 2/4] Refactor Jenkinsfile to simplify agent configuration by changing from a specific Docker label to 'any', allowing for more flexible build agent selection. --- Jenkinsfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 8a5be402..956c454c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,5 @@ pipeline { - agent { - label 'docker' - } + agent any options { timestamps() disableConcurrentBuilds() From 49d635b79a2cd907a6335c7618ca9e1d51cf7270 Mon Sep 17 00:00:00 2001 From: AlexeerCT Date: Thu, 2 Apr 2026 11:24:04 -0500 Subject: [PATCH 3/4] Refactor Jenkinsfile to improve SSH known hosts handling. Introduced a variable for the development server host and updated the known hosts file path, enhancing security and maintainability during deployment processes. --- Jenkinsfile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 956c454c..321fead5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -248,19 +248,20 @@ pipeline { ) ]) { script { + def devHost = env.DEV_SERVER_HOST?.trim() + def knownHostsFile = "${env.WORKSPACE}/.jenkins-anexo76-known_hosts" sh """ set -euo pipefail - mkdir -p ~/.ssh - chmod 700 ~/.ssh - ssh-keyscan -T 15 -H '${env.DEV_SERVER_HOST}' >> ~/.ssh/known_hosts + ssh-keyscan -T 15 -H '${devHost}' > '${knownHostsFile}' """ def remote = [ name: 'dev-server', - host: env.DEV_SERVER_HOST, + host: devHost, user: env.DEV_SERVER_USER ?: env.DEV_SERVER_SSH_USER, identityFile: env.DEV_SERVER_KEY, passphrase: env.DEV_SERVER_KEY_PASSPHRASE, - allowAnyHosts: false + allowAnyHosts: false, + knownHosts: knownHostsFile ] sshCommand remote: remote, command: ''' set -euo pipefail From 4bc38b691f59d665adf9dde00751d28ad582f58b Mon Sep 17 00:00:00 2001 From: AlexeerCT Date: Thu, 2 Apr 2026 11:31:00 -0500 Subject: [PATCH 4/4] Add APP_VERSION handling in Dockerfile and config.py Updated the Dockerfile to include an ARG and ENV for APP_VERSION, allowing for version specification during build time. Modified config.py to set a default APP_VERSION, which can be overridden by the environment, enhancing flexibility for deployment and runtime configuration. --- backend/Dockerfile | 4 ++++ backend/core/config.py | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 6f6a9c20..84d0d41d 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -45,6 +45,10 @@ RUN pip install --no-cache-dir -r requirements.txt # Copiar código COPY . . +# Jenkins / CI pasan --build-arg APP_VERSION=…; debe quedar en ENV para runtime (API /version, OpenAPI, etc.) +ARG APP_VERSION=dev-local +ENV APP_VERSION=${APP_VERSION} + # Exponer puerto EXPOSE 8000 diff --git a/backend/core/config.py b/backend/core/config.py index eaabdb82..3fad873f 100644 --- a/backend/core/config.py +++ b/backend/core/config.py @@ -2,7 +2,6 @@ Configuración centralizada de la aplicación usando Pydantic Settings """ -import os from typing import List from pydantic import field_validator @@ -14,9 +13,8 @@ class Settings(BaseSettings): # Application APP_NAME: str = "Anexo76" - # La versión se obtiene de la variable de entorno APP_VERSION que se pasa desde Docker - # Si no existe, usa un valor por defecto de desarrollo - APP_VERSION: str = os.getenv("APP_VERSION", "dev-local") + # Sobreescribible con APP_VERSION (Dockerfile/Jenkins: build-arg + ENV) o entorno en runtime + APP_VERSION: str = "dev-local" DEBUG: bool = True ENVIRONMENT: str = "development"