diff --git a/Jenkinsfile b/Jenkinsfile index 3d5615b..0707c15 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,144 +1,141 @@ pipeline { agent any + parameters { + string(name: 'PROJECT_NAME', defaultValue: 'portal-app', description: 'Nome do projeto') + string(name: 'GIT_BRANCH', defaultValue: 'main', description: 'Branch') + } + environment { - DEPLOY_USER = "jenkins" - DEPLOY_SERVER = "192.168.1.81" - IMAGE_NAME = "portal-app" - CONTAINER_NAME = "portal-app" - REMOTE_PATH = "/home/jenkins/app" + FORGEJO_HOST = '175.15.15.55' + PODMAN_HOST = '175.15.15.12' + BASE_DIR = '/usr/app' } stages { - stage('Testar Conexão SSH') { + stage('Sync Código no Podman Host') { steps { - sshagent(credentials: ['chave-ssh-id']) { - sh "ssh -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_SERVER} 'echo Conexão SSH bem-sucedida!'" - } - } - } - - stage('Ver chave pública usada') { - steps { - sshagent(credentials: ['chave-ssh-id']) { - sh 'ssh-add -L' - } - } - } - - stage('Checkout Código (local, só para pegar commit hash)') { - steps { - git branch: 'main', url: 'https://forgeo-olymp.duckdns.org/rayankonecny/portal-app.git' // usando IP local por confiabilidade - script { - env.GIT_COMMIT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim() - env.BUILD_TAG = "${env.BUILD_NUMBER}-${env.GIT_COMMIT}" - echo "BUILD_TAG definido como: ${env.BUILD_TAG}" - } - } - } - - stage('Clonar Projeto e Criar Dockerfile no Servidor') { - steps { - sshagent(credentials: ['chave-ssh-id']) { - script { - sh """ -ssh -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_SERVER} ' - set -e - - mkdir -p ${REMOTE_PATH} - git config --global --add safe.directory ${REMOTE_PATH} - rm -rf ${REMOTE_PATH} - - git clone https://forgeo-olymp.duckdns.org/rayankonecny/portal-app.git ${REMOTE_PATH} - - if [ -f ${REMOTE_PATH}/.env ]; then - echo "[INFO] Removendo NODE_ENV do .env para evitar erro com Vite" - sed -i "/NODE_ENV/d" ${REMOTE_PATH}/.env - fi - - cat < ${REMOTE_PATH}/Dockerfile -FROM node:18-alpine - -WORKDIR /app - -COPY package*.json ./ - -RUN npm install - -COPY . . - -EXPOSE 5173 - -CMD ["npm", "run", "dev"] -EOF -' - """ - } - } - } - } - - stage('Build da Imagem Podman') { - steps { - sshagent(credentials: ['chave-ssh-id']) { + sshagent(['root-ssh-key']) { sh """ -ssh -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_SERVER} ' - cd ${REMOTE_PATH} && - podman build -t ${IMAGE_NAME}:${BUILD_TAG} -t ${IMAGE_NAME}:latest . -' + ssh root@${PODMAN_HOST} ' + set -e + PROJECT_DIR=${BASE_DIR}/${params.PROJECT_NAME} + + mkdir -p \$PROJECT_DIR + mkdir -p \$PROJECT_DIR/.deploy + + if [ ! -d "\$PROJECT_DIR/.git" ]; then + git clone -b ${params.GIT_BRANCH} ssh://root@${FORGEJO_HOST}:/rayankonecny/portal-app.git \$PROJECT_DIR + else + cd \$PROJECT_DIR + git fetch origin + git reset --hard origin/${params.GIT_BRANCH} + fi + ' """ } } } - stage('Parar e Remover Container Antigo') { + stage('Detectar Mudanças') { steps { - sshagent(credentials: ['chave-ssh-id']) { + sshagent(['root-ssh-key']) { sh """ -ssh ${DEPLOY_USER}@${DEPLOY_SERVER} ' - podman stop ${CONTAINER_NAME} || true && - podman rm ${CONTAINER_NAME} || true -' + ssh root@${PODMAN_HOST} ' + set -e + cd ${BASE_DIR}/${params.PROJECT_NAME} + + LAST_FILE=.deploy/last_commit + CURRENT=\$(git rev-parse HEAD) + + if [ -f \$LAST_FILE ]; then + PREV=\$(cat \$LAST_FILE) + else + PREV="" + fi + + echo "Commit anterior: \$PREV" + echo "Commit atual: \$CURRENT" + + git diff --name-only \$PREV \$CURRENT > .deploy/changed_files || true + cat .deploy/changed_files + + awk -F/ "/^services/ {print \\$2}" .deploy/changed_files | sort -u > .deploy/changed_services + cat .deploy/changed_services + ' """ } } } - stage('Deploy no Servidor') { + stage('Build Inteligente (somente serviços alterados)') { steps { - sshagent(credentials: ['chave-ssh-id']) { + sshagent(['root-ssh-key']) { sh """ -ssh ${DEPLOY_USER}@${DEPLOY_SERVER} ' - podman run -d --network host --name ${CONTAINER_NAME} ${IMAGE_NAME}:${BUILD_TAG} -' + ssh root@${PODMAN_HOST} ' + set -e + cd ${BASE_DIR}/${params.PROJECT_NAME} + + if [ ! -s .deploy/changed_services ]; then + echo "Nenhum serviço alterado. Pulando build." + exit 0 + fi + + while read SERVICE; do + echo "🔨 Buildando serviço: \$SERVICE" + podman build -t ${params.PROJECT_NAME}-\$SERVICE:latest services/\$SERVICE + done < .deploy/changed_services + ' """ } } } - stage('Verificar Status do Container') { + stage('Deploy Seletivo via podman-compose') { steps { - sshagent(credentials: ['chave-ssh-id']) { + sshagent(['root-ssh-key']) { sh """ -ssh ${DEPLOY_USER}@${DEPLOY_SERVER} ' - podman ps | grep ${CONTAINER_NAME} || echo "Container não encontrado" -' + ssh root@${PODMAN_HOST} ' + set -e + cd ${BASE_DIR}/${params.PROJECT_NAME} + + if [ -s .deploy/changed_services ]; then + while read SERVICE; do + echo "🚀 Reiniciando serviço: \$SERVICE" + podman-compose up -d --no-deps --build \$SERVICE + done < .deploy/changed_services + else + echo "Nada para subir" + fi + + git rev-parse HEAD > .deploy/last_commit + ' """ } } } - stage('Limpeza de Imagens Antigas') { + stage('Observabilidade') { steps { - sshagent(credentials: ['chave-ssh-id']) { + sshagent(['root-ssh-key']) { sh """ -ssh ${DEPLOY_USER}@${DEPLOY_SERVER} ' - podman image prune -f -' + ssh root@${PODMAN_HOST} ' + echo "Containers ativos:" + podman ps + ' """ } } } } + + post { + success { + echo "✅ Deploy inteligente concluído para ${params.PROJECT_NAME}" + } + failure { + echo "❌ Falha no deploy" + } + } }