pipeline { agent any environment { DEPLOY_USER = "jenkins" DEPLOY_SERVER = "192.168.1.81" IMAGE_NAME = "astro-app" CONTAINER_NAME = "astro-app" REMOTE_PATH = "/home/jenkins/app" SONAR_PROJECT_KEY = "astro-app" DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/1328166779662372884/YtUDw2ADESmRw1V5xuA9fOF7ZZujXtHpLAzmaQn99TPKI3rfLmH8ApLQw9mn8d9G6dSb" } options { timestamps() } parameters { booleanParam(name: 'ROLLBACK', defaultValue: false, description: 'Executar rollback da produção?') } stages { stage('Commit') { steps { echo 'Pipeline iniciado via push ou merge.' } } stage('Checkout Código') { steps { git branch: 'main', url: 'https://forgeo-olymp.duckdns.org/rayankonecny/portal-app.git' 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('Build') { steps { sh 'npm ci' sh 'npm run build || echo "Build não crítico"' } } stage('Test') { steps { sh 'npm test || echo "Testes não críticos"' } } stage('SonarQube Analysis') { environment { SONAR_SCANNER_HOME = tool 'SonarQube Scanner' } steps { withSonarQubeEnv('SonarQube') { sh """ ${SONAR_SCANNER_HOME}/bin/sonar-scanner \ -Dsonar.projectKey=${SONAR_PROJECT_KEY} \ -Dsonar.sources=. \ -Dsonar.host.url=$SONAR_HOST_URL \ -Dsonar.login=$SONAR_AUTH_TOKEN """ } } } stage('Deploy to Stage') { steps { sshagent(credentials: ['chave-ssh-id']) { sh """ ssh ${DEPLOY_USER}@${DEPLOY_SERVER} " set -e rm -rf ${REMOTE_PATH} git clone https://forgeo-olymp.duckdns.org/rayankonecny/portal-app.git ${REMOTE_PATH} cd ${REMOTE_PATH} sed -i '/NODE_ENV/d' .env || true cat < Dockerfile FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 5173 CMD [\\"npm\\", \\"run\\", \\"dev\\"] EOF podman build -t ${IMAGE_NAME}:${BUILD_TAG}-stage -t ${IMAGE_NAME}:stage . podman stop ${CONTAINER_NAME}-stage || true podman rm ${CONTAINER_NAME}-stage || true podman run -d --network host --name ${CONTAINER_NAME}-stage ${IMAGE_NAME}:${BUILD_TAG}-stage " """ } } } stage('Aprovação para Produção') { steps { input message: "Aprovar o deploy em produção?", ok: "Deployar" } } stage('Deploy to Production') { steps { sshagent(credentials: ['chave-ssh-id']) { sh """ ssh ${DEPLOY_USER}@${DEPLOY_SERVER} " echo '${BUILD_TAG}' > ${REMOTE_PATH}/last_successful_build.txt cd ${REMOTE_PATH} podman build -t ${IMAGE_NAME}:${BUILD_TAG}-prod -t ${IMAGE_NAME}:prod . podman stop ${CONTAINER_NAME} || true podman rm ${CONTAINER_NAME} || true podman run -d --network host --name ${CONTAINER_NAME} ${IMAGE_NAME}:${BUILD_TAG}-prod " """ } } } stage('Verificar Produção') { steps { sshagent(credentials: ['chave-ssh-id']) { sh """ ssh ${DEPLOY_USER}@${DEPLOY_SERVER} " podman ps | grep ${CONTAINER_NAME} || echo 'Container não encontrado' " """ } } } stage('Rollback Manual') { when { expression { return params.ROLLBACK == true } } steps { sshagent(credentials: ['chave-ssh-id']) { sh """ ssh ${DEPLOY_USER}@${DEPLOY_SERVER} " TAG=\$(cat ${REMOTE_PATH}/last_successful_build.txt || echo 'latest') echo '[INFO] Restaurando versão: \$TAG' podman stop ${CONTAINER_NAME} || true podman rm ${CONTAINER_NAME} || true podman run -d --network host --name ${CONTAINER_NAME} ${IMAGE_NAME}:\$TAG " """ } } } stage('Limpeza de Imagens Antigas') { steps { sshagent(credentials: ['chave-ssh-id']) { sh """ ssh ${DEPLOY_USER}@${DEPLOY_SERVER} "podman image prune -f" """ } } } } post { success { script { sh """ curl -H "Content-Type: application/json" -X POST \ -d '{"content": "✅ *Deploy bem-sucedido!* Projeto: ${IMAGE_NAME}, Versão: ${BUILD_TAG}"}' \ ${DISCORD_WEBHOOK_URL} """ } } failure { script { sh """ curl -H "Content-Type: application/json" -X POST \ -d '{"content": "❌ *Deploy falhou!* Projeto: ${IMAGE_NAME}, Build: ${BUILD_TAG}"}' \ ${DISCORD_WEBHOOK_URL} """ } } } }