Files
plantillas-proyectos/Jenkinsfile

255 lines
8.8 KiB
Groovy

pipeline {
agent any
options {
timestamps()
disableConcurrentBuilds()
}
parameters {
choice(name: 'ENVIRONMENT', choices: ['development', 'main'], description: 'Rama a ejecutar')
string(name: 'VERSION_MAYOR', defaultValue: '1', description: 'Componente mayor de versión')
}
environment {
REGISTRY = 'dev.aduanasoft.com'
IMAGE_NAMESPACE = 'anexo76'
}
stages {
stage('Preflight tools') {
steps {
sh '''
set -euo pipefail
echo "Node: $(hostname)"
if ! command -v docker >/dev/null 2>&1; then
echo "ERROR: este agente no tiene docker instalado."
echo "Usa un nodo Jenkins con label 'docker' y acceso al daemon."
exit 1
fi
docker --version
'''
}
}
stage('Checkout') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "*/${params.ENVIRONMENT}"]],
userRemoteConfigs: [[
url: 'https://git.aduanasoft.com/ADUANASOFT/anexo76.git',
credentialsId: 'gitea_acazares'
]],
extensions: [[$class: 'CloneOption', depth: 0, shallow: false]]
])
}
}
stage('Test backend') {
steps {
sh '''
set -euxo pipefail
echo "== Test backend stage started =="
echo "Workspace: $WORKSPACE"
docker --version
DB_CONTAINER="anexo76-test-db-${BUILD_NUMBER}"
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}"
cleanup() {
docker rm -f "$PY_CONTAINER" >/dev/null 2>&1 || true
docker rm -f "$DB_CONTAINER" >/dev/null 2>&1 || true
docker network rm "$TEST_NETWORK" >/dev/null 2>&1 || true
}
trap cleanup EXIT
docker network rm "$TEST_NETWORK" >/dev/null 2>&1 || true
docker network create "$TEST_NETWORK"
docker rm -f "$DB_CONTAINER" >/dev/null 2>&1 || true
docker run -d --name "$DB_CONTAINER" \
--network "$TEST_NETWORK" \
-e POSTGRES_DB="$DB_NAME" \
-e POSTGRES_USER="$DB_USER" \
-e POSTGRES_PASSWORD="$DB_PASS" \
postgres:16-alpine
# Espera a que Postgres acepte conexiones
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
break
fi
sleep 2
done
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;
CREATE SCHEMA IF NOT EXISTS a24;
CREATE SCHEMA IF NOT EXISTS a76;
CREATE SCHEMA IF NOT EXISTS public;
"
docker rm -f "$PY_CONTAINER" >/dev/null 2>&1 || true
docker run -d --name "$PY_CONTAINER" --network "$TEST_NETWORK" python:3.12-slim sleep infinity
docker exec "$PY_CONTAINER" mkdir -p /workspace
docker cp "$WORKSPACE/." "$PY_CONTAINER:/workspace"
docker exec \
-e TEST_DATABASE_URL="$TEST_DATABASE_URL" \
"$PY_CONTAINER" \
sh -lc '
set -euxo pipefail
python --version
python -m pip install --upgrade pip
if [ -f /workspace/backend/requirements.txt ]; then
pip install -r /workspace/backend/requirements.txt
elif [ -f /workspace/backend/requirements/base.txt ]; then
pip install -r /workspace/backend/requirements/base.txt
else
echo "ERROR: No requirements file found in /workspace/backend"
ls -la /workspace || true
ls -la /workspace/backend || true
ls -la /workspace/backend/requirements || true
exit 1
fi
cd /workspace/backend
alembic upgrade head
pytest -q tests -v -ra -s
'
'''
}
}
stage('Generate version') {
steps {
script {
def year = sh(returnStdout: true, script: 'date +%y').trim()
def month = sh(returnStdout: true, script: 'date +%m').trim()
if (params.ENVIRONMENT == 'development') {
def shortHash = sh(returnStdout: true, script: 'git rev-parse --short=8 HEAD').trim()
env.APP_VERSION = "${year}.${month}.${params.VERSION_MAYOR}.${shortHash}"
} else {
def lastTag = sh(returnStdout: true, script: 'git describe --tags --abbrev=0 2>/dev/null || true').trim()
def commitCount = lastTag
? sh(returnStdout: true, script: "git rev-list ${lastTag}..HEAD --count").trim()
: sh(returnStdout: true, script: 'git rev-list --count HEAD').trim()
if (commitCount == '0') {
commitCount = '1'
}
env.APP_VERSION = "${year}.${month}.${params.VERSION_MAYOR}.${commitCount}"
}
echo "VERSION: ${env.APP_VERSION}"
}
}
}
stage('Docker login') {
steps {
withCredentials([usernamePassword(credentialsId: 'harbor-credentials', usernameVariable: 'HARBOR_USERNAME', passwordVariable: 'HARBOR_PASSWORD')]) {
sh '''
set -euo pipefail
echo "$HARBOR_PASSWORD" | docker login "$REGISTRY" -u "$HARBOR_USERNAME" --password-stdin
'''
}
}
}
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
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
docker push "${REGISTRY}/${IMAGE_NAMESPACE}/frontend:latest"
'''
}
}
stage('Tag release (main)') {
when {
expression { params.ENVIRONMENT == 'main' }
}
steps {
withCredentials([usernamePassword(credentialsId: 'gitea_acazares', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]) {
sh '''
set -euo pipefail
git -c user.name='Jenkins' -c user.email='jenkins@gitea.local' \
tag -a "v${APP_VERSION}" -m "Release version ${APP_VERSION}"
git remote set-url origin "https://${GIT_USERNAME}:${GIT_PASSWORD}@git.aduanasoft.com/ADUANASOFT/anexo76.git"
git push origin "v${APP_VERSION}"
'''
}
}
}
stage('Deploy development') {
when {
expression { params.ENVIRONMENT == 'development' }
}
steps {
withCredentials([
usernamePassword(credentialsId: 'harbor-credentials', usernameVariable: 'HARBOR_USERNAME', passwordVariable: 'HARBOR_PASSWORD'),
string(credentialsId: 'dev-server-user', variable: 'DEV_SERVER_USER'),
string(credentialsId: 'dev-server-host', variable: 'DEV_SERVER_HOST'),
sshUserPrivateKey(
credentialsId: 'dev-server-ssh',
keyFileVariable: 'DEV_SERVER_KEY',
usernameVariable: 'DEV_SERVER_SSH_USER',
passphraseVariable: 'DEV_SERVER_KEY_PASSPHRASE'
)
]) {
script {
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
]
sshCommand remote: remote, command: '''
set -euo pipefail
cd ~/projects/anexo76
# 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
docker image prune -f
'''
}
}
}
}
}
}