pipeline { agent any options { disableConcurrentBuilds() } environment { DEPLOY_USER = 'hskwon' RELEASE_ID = new Date().format('yyyyMMdd_HHmmss') } stages { stage('Checkout') { steps { checkout scm script { env.GIT_COMMIT_MSG = sh(script: "git log -1 --pretty=format:'%s'", returnStdout: true).trim() } slackSend channel: '#product_infra', color: '#439FE0', tokenCredentialId: 'slack-token', message: "πŸš€ *react* λΉŒλ“œ μ‹œμž‘ (`${env.BRANCH_NAME}`)\n${env.GIT_COMMIT_MSG}\n<${env.BUILD_URL}|λΉŒλ“œ #${env.BUILD_NUMBER}>" } } stage('Prepare Env') { steps { script { if (env.BRANCH_NAME == 'main') { // main: Stage λΉŒλ“œ λ¨Όμ € (승인 ν›„ Production μž¬λΉŒλ“œ) sh "cp /var/lib/jenkins/env-files/react/.env.stage .env.production" } else { def envFile = "/var/lib/jenkins/env-files/react/.env.${env.BRANCH_NAME}" sh "cp ${envFile} .env.production" } } } } stage('Install') { steps { sh 'npm install --prefer-offline' } } stage('Build') { steps { sh 'npm run build' } } // ── develop β†’ κ°œλ°œμ„œλ²„ 배포 ── stage('Deploy Development') { when { branch 'develop' } steps { sshagent(credentials: ['deploy-ssh-key']) { sh """ rsync -az --delete \ --exclude='.git' --exclude='.env*' --exclude='ecosystem.config.*' \ .next package.json next.config.ts public node_modules \ ${DEPLOY_USER}@114.203.209.83:/home/webservice/react/ scp .env.production ${DEPLOY_USER}@114.203.209.83:/home/webservice/react/.env.production ssh ${DEPLOY_USER}@114.203.209.83 'cd /home/webservice/react && pm2 restart sam-react' """ } } } // ── main β†’ μš΄μ˜μ„œλ²„ Stage 배포 ── stage('Deploy Stage') { when { branch 'main' } steps { sshagent(credentials: ['deploy-ssh-key']) { sh """ ssh ${DEPLOY_USER}@211.117.60.189 'mkdir -p /home/webservice/react-stage/releases/${RELEASE_ID}' rsync -az --delete \ .next package.json next.config.ts public node_modules \ ${DEPLOY_USER}@211.117.60.189:/home/webservice/react-stage/releases/${RELEASE_ID}/ scp .env.production ${DEPLOY_USER}@211.117.60.189:/home/webservice/react-stage/releases/${RELEASE_ID}/.env.production ssh ${DEPLOY_USER}@211.117.60.189 ' ln -sfn /home/webservice/react-stage/releases/${RELEASE_ID} /home/webservice/react-stage/current && cd /home/webservice && pm2 reload sam-front-stage 2>/dev/null || pm2 start react-stage/current/node_modules/.bin/next --name sam-front-stage -- start -p 3100 && cd /home/webservice/react-stage/releases && ls -1dt */ | tail -n +4 | xargs rm -rf 2>/dev/null || true ' """ } } } // ── 운영 배포 승인 ── stage('Production Approval') { when { branch 'main' } steps { slackSend channel: '#product_deploy', color: '#FF9800', tokenCredentialId: 'slack-token', message: "πŸ”” *react* 운영 배포 승인 λŒ€κΈ° 쀑\n${env.GIT_COMMIT_MSG}\nStage: https://stage.sam.it.kr\n<${env.BUILD_URL}input|μŠΉμΈν•˜λŸ¬ κ°€κΈ°>" timeout(time: 24, unit: 'HOURS') { input message: 'Stage 확인 ν›„ 운영 배포λ₯Ό μ§„ν–‰ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?\nStage: https://stage.sam.it.kr', ok: '운영 배포 μ§„ν–‰' } } } // ── main β†’ Production μž¬λΉŒλ“œ (운영 ν™˜κ²½λ³€μˆ˜) ── stage('Rebuild for Production') { when { branch 'main' } steps { sh "cp /var/lib/jenkins/env-files/react/.env.main .env.production" sh 'npm run build' } } // ── main β†’ μš΄μ˜μ„œλ²„ Production 배포 ── stage('Deploy Production') { when { branch 'main' } steps { sshagent(credentials: ['deploy-ssh-key']) { sh """ ssh ${DEPLOY_USER}@211.117.60.189 'mkdir -p /home/webservice/react/releases/${RELEASE_ID}' rsync -az --delete \ .next package.json next.config.ts public node_modules \ ${DEPLOY_USER}@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/ scp .env.production ${DEPLOY_USER}@211.117.60.189:/home/webservice/react/releases/${RELEASE_ID}/.env.production ssh ${DEPLOY_USER}@211.117.60.189 ' ln -sfn /home/webservice/react/releases/${RELEASE_ID} /home/webservice/react/current && cd /home/webservice && pm2 reload sam-front && cd /home/webservice/react/releases && ls -1dt */ | tail -n +6 | xargs rm -rf 2>/dev/null || true ' """ } } } } post { success { slackSend channel: '#product_infra', color: 'good', tokenCredentialId: 'slack-token', message: "βœ… *react* 배포 성곡 (`${env.BRANCH_NAME}`)\n${env.GIT_COMMIT_MSG}\n<${env.BUILD_URL}|λΉŒλ“œ #${env.BUILD_NUMBER}>" } failure { slackSend channel: '#product_infra', color: 'danger', tokenCredentialId: 'slack-token', message: "❌ *react* 배포 μ‹€νŒ¨ (`${env.BRANCH_NAME}`)\n${env.GIT_COMMIT_MSG}\n<${env.BUILD_URL}|λΉŒλ“œ #${env.BUILD_NUMBER}>" } } }