feature/jenkins-integration
This commit is contained in:
258
Jenkinsfile
vendored
Normal file
258
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
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:8443'
|
||||||
|
IMAGE_NAMESPACE = 'panel-bases-anexo24'
|
||||||
|
}
|
||||||
|
|
||||||
|
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/PANEL_BASES_ANEXO24.git',
|
||||||
|
credentialsId: 'gitea_acazares'
|
||||||
|
]],
|
||||||
|
extensions: [[$class: 'CloneOption', depth: 0, shallow: false]]
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Test') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
set -euxo pipefail
|
||||||
|
echo "== Test (svelte-check + tsc) =="
|
||||||
|
|
||||||
|
FE_CONTAINER="panel-bases-test-${BUILD_NUMBER}"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
docker rm -f "$FE_CONTAINER" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
docker rm -f "$FE_CONTAINER" >/dev/null 2>&1 || true
|
||||||
|
docker run -d --name "$FE_CONTAINER" -w /workspace node:20-alpine sleep infinity
|
||||||
|
docker exec "$FE_CONTAINER" mkdir -p /workspace
|
||||||
|
docker cp "$WORKSPACE/." "$FE_CONTAINER:/workspace"
|
||||||
|
|
||||||
|
docker exec "$FE_CONTAINER" sh -lc '
|
||||||
|
set -euxo pipefail
|
||||||
|
node --version
|
||||||
|
npm config set strict-ssl false
|
||||||
|
npm ci --legacy-peer-deps
|
||||||
|
npm run check
|
||||||
|
'
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
retry(3) {
|
||||||
|
sh '''
|
||||||
|
set -euo pipefail
|
||||||
|
export DOCKER_BUILDKIT=1
|
||||||
|
docker build \
|
||||||
|
-t "${REGISTRY}/${IMAGE_NAMESPACE}/app:${APP_VERSION}" \
|
||||||
|
-t "${REGISTRY}/${IMAGE_NAMESPACE}/app:latest" \
|
||||||
|
-f ./Dockerfile \
|
||||||
|
.
|
||||||
|
|
||||||
|
docker push "${REGISTRY}/${IMAGE_NAMESPACE}/app:${APP_VERSION}"
|
||||||
|
docker push "${REGISTRY}/${IMAGE_NAMESPACE}/app: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/PANEL_BASES_ANEXO24.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: 'panel-server-user', variable: 'PANEL_SERVER_USER'),
|
||||||
|
string(credentialsId: 'panel-server-host', variable: 'PANEL_SERVER_HOST'),
|
||||||
|
sshUserPrivateKey(
|
||||||
|
credentialsId: 'panel-server-ssh',
|
||||||
|
keyFileVariable: 'PANEL_SERVER_KEY',
|
||||||
|
usernameVariable: 'PANEL_SERVER_SSH_USER',
|
||||||
|
passphraseVariable: 'PANEL_SERVER_KEY_PASSPHRASE'
|
||||||
|
)
|
||||||
|
]) {
|
||||||
|
/*
|
||||||
|
* Destino: Windows con OpenSSH Server. El agente Jenkins es Linux (bash).
|
||||||
|
* En bash, C:\rutas\ con \ dentro de "..." se corrompen → usar siempre C:/ruta (válido en PowerShell).
|
||||||
|
* DOCKER_CONFIG local sin wincred para evitar errores de "logon session".
|
||||||
|
* ssh-agent + ssh-add para clave con passphrase.
|
||||||
|
* StrictHostKeyChecking=accept-new con known_hosts en workspace para idempotencia.
|
||||||
|
*/
|
||||||
|
sh '''
|
||||||
|
set -euo pipefail
|
||||||
|
WIN_DOCKER_CFG='C:/Aduanasoft/docker-jenkins-ci'
|
||||||
|
WIN_APP_PROD='C:/Aduanasoft/panel_bases_prod'
|
||||||
|
chmod 600 "${PANEL_SERVER_KEY}" || true
|
||||||
|
eval "$(ssh-agent -s)"
|
||||||
|
trap 'kill "${SSH_AGENT_PID}" 2>/dev/null || true' EXIT
|
||||||
|
if [ -n "${PANEL_SERVER_KEY_PASSPHRASE}" ]; then
|
||||||
|
printf '%s\n' "${PANEL_SERVER_KEY_PASSPHRASE}" | ssh-add "${PANEL_SERVER_KEY}"
|
||||||
|
else
|
||||||
|
ssh-add "${PANEL_SERVER_KEY}"
|
||||||
|
fi
|
||||||
|
KNOWN="${WORKSPACE}/.jenkins-panel-known_hosts"
|
||||||
|
: > "${KNOWN}"
|
||||||
|
SSHOPTS="-o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=${KNOWN}"
|
||||||
|
SSH_U="${PANEL_SERVER_USER}"
|
||||||
|
if [ -z "${SSH_U}" ]; then SSH_U="${PANEL_SERVER_SSH_USER}"; fi
|
||||||
|
|
||||||
|
# Paso 1: inicializar DOCKER_CONFIG limpio (sin credsStore/wincred)
|
||||||
|
DOCKER_CFG_PS_TEMPLATE="$(cat <<'EOF'
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$cfg = "_CFG_"
|
||||||
|
New-Item -ItemType Directory -Force -Path $cfg | Out-Null
|
||||||
|
Remove-Item -Path (Join-Path $cfg "config.json") -Force -ErrorAction SilentlyContinue
|
||||||
|
Set-Content -Path (Join-Path $cfg "config.json") -Value '{"auths":{}}' -Encoding Ascii -NoNewline
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
DOCKER_CFG_PS_CMD="$(printf '%s' "${DOCKER_CFG_PS_TEMPLATE}" | sed "s|_CFG_|${WIN_DOCKER_CFG}|g")"
|
||||||
|
DOCKER_CFG_PS_B64="$(printf '%s' "${DOCKER_CFG_PS_CMD}" | iconv -f UTF-8 -t UTF-16LE | base64 -w 0)"
|
||||||
|
ssh ${SSHOPTS} "${SSH_U}@${PANEL_SERVER_HOST}" \
|
||||||
|
powershell.exe -NoProfile -NonInteractive -EncodedCommand "${DOCKER_CFG_PS_B64}"
|
||||||
|
|
||||||
|
# Paso 2: escribir auth de Harbor directo en config.json (evita docker login + wincred)
|
||||||
|
if [ -z "${HARBOR_PASSWORD:-}" ]; then
|
||||||
|
echo "ERROR: HARBOR_PASSWORD vacío en Jenkins credentials"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
set +x
|
||||||
|
HARBOR_PASSWORD_B64="$(printf '%s' "${HARBOR_PASSWORD}" | base64 -w 0)"
|
||||||
|
DOCKER_AUTH_PS_TEMPLATE="$(cat <<'EOF'
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$cfg = "_CFG_"
|
||||||
|
$usr = "_USER_"
|
||||||
|
$reg = "_REG_"
|
||||||
|
$pwdB64 = "_PWD_B64_"
|
||||||
|
$pwd = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($pwdB64))
|
||||||
|
if ([string]::IsNullOrWhiteSpace($pwd)) { throw "HARBOR_PASSWORD vacio o invalido." }
|
||||||
|
$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${usr}:$pwd"))
|
||||||
|
$json = "{""auths"":{""$reg"":{""auth"":""$auth""}}}"
|
||||||
|
Set-Content -Path (Join-Path $cfg "config.json") -Value $json -Encoding Ascii -NoNewline
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
DOCKER_AUTH_PS_CMD="$(printf '%s' "${DOCKER_AUTH_PS_TEMPLATE}" \
|
||||||
|
| sed "s|_CFG_|${WIN_DOCKER_CFG}|g" \
|
||||||
|
| sed "s|_USER_|${HARBOR_USERNAME}|g" \
|
||||||
|
| sed "s|_REG_|${REGISTRY}|g" \
|
||||||
|
| sed "s|_PWD_B64_|${HARBOR_PASSWORD_B64}|g")"
|
||||||
|
AUTH_PS_B64="$(printf '%s' "${DOCKER_AUTH_PS_CMD}" | iconv -f UTF-8 -t UTF-16LE | base64 -w 0)"
|
||||||
|
ssh ${SSHOPTS} "${SSH_U}@${PANEL_SERVER_HOST}" \
|
||||||
|
powershell.exe -NoProfile -NonInteractive -EncodedCommand "${AUTH_PS_B64}"
|
||||||
|
|
||||||
|
# Paso 3: pull + up + prune
|
||||||
|
DOCKER_COMPOSE_PS_TEMPLATE="$(cat <<'EOF'
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$env:DOCKER_CONFIG = "_CFG_"
|
||||||
|
Set-Location "_APP_PROD_"
|
||||||
|
docker compose -f docker-compose.prod.yml pull
|
||||||
|
docker compose -f docker-compose.prod.yml up -d
|
||||||
|
docker image prune -f
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
DOCKER_COMPOSE_PS_CMD="$(printf '%s' "${DOCKER_COMPOSE_PS_TEMPLATE}" \
|
||||||
|
| sed "s|_CFG_|${WIN_DOCKER_CFG}|g" \
|
||||||
|
| sed "s|_APP_PROD_|${WIN_APP_PROD}|g")"
|
||||||
|
COMPOSE_PS_B64="$(printf '%s' "${DOCKER_COMPOSE_PS_CMD}" | iconv -f UTF-8 -t UTF-16LE | base64 -w 0)"
|
||||||
|
set -x
|
||||||
|
ssh ${SSHOPTS} "${SSH_U}@${PANEL_SERVER_HOST}" \
|
||||||
|
powershell.exe -NoProfile -NonInteractive -EncodedCommand "${COMPOSE_PS_B64}"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
docker-compose.prod.yml
Normal file
47
docker-compose.prod.yml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: dev.aduanasoft.com:8443/panel-bases-anexo24/app:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
# SQL Server: credenciales globales (server_name/database_name vienen de a24c.database_nodes en Postgres)
|
||||||
|
- PANEL_MSSQL_USER=${PANEL_MSSQL_USER}
|
||||||
|
- PANEL_MSSQL_PASSWORD=${PANEL_MSSQL_PASSWORD}
|
||||||
|
- PANEL_MSSQL_DOCKER=true
|
||||||
|
- IN_DOCKER=true
|
||||||
|
|
||||||
|
# PostgreSQL: BD externa (ControlDesk), accesible desde el host
|
||||||
|
- DB_POSTGRES_HOST=host.docker.internal
|
||||||
|
- DB_POSTGRES_PORT=${DB_POSTGRES_PORT:-5432}
|
||||||
|
- DB_POSTGRES_USER=${DB_POSTGRES_USER}
|
||||||
|
- DB_POSTGRES_PASS=${DB_POSTGRES_PASS}
|
||||||
|
- DB_POSTGRES_DB=${DB_POSTGRES_DB}
|
||||||
|
|
||||||
|
# JWT
|
||||||
|
- JWT_SECRET=${JWT_SECRET}
|
||||||
|
|
||||||
|
# Ruta de respaldos montada desde el host
|
||||||
|
- BACKUP_PATH=/data/backups
|
||||||
|
|
||||||
|
- PORT=3000
|
||||||
|
- ORIGIN=${ORIGIN}
|
||||||
|
- NODE_ENV=production
|
||||||
|
|
||||||
|
# SMTP para avisos (opcional — vacío deshabilita el envío sin errores)
|
||||||
|
- SMTP_HOST=${SMTP_HOST:-}
|
||||||
|
- SMTP_PORT=${SMTP_PORT:-587}
|
||||||
|
- SMTP_USER=${SMTP_USER:-}
|
||||||
|
- SMTP_PASSWORD=${SMTP_PASSWORD:-}
|
||||||
|
- SMTP_FROM=${SMTP_FROM:-}
|
||||||
|
- SMTP_USE_TLS=${SMTP_USE_TLS:-true}
|
||||||
|
volumes:
|
||||||
|
- ${BACKUP_HOST_PATH}:/data/backups
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
networks:
|
||||||
|
- aduanasoft-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
aduanasoft-network:
|
||||||
|
external: true
|
||||||
Reference in New Issue
Block a user