update: output 변수 규칙에 맞도록 수정 #8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Deploy to Production Server | |
on: | |
push: | |
branches: | |
- main | |
jobs: | |
set: | |
environment: Deploy-Prod | |
runs-on: ubuntu-latest | |
outputs: | |
TIMESTAMP: ${{ steps.set_timestamp.outputs.TIMESTAMP }} | |
IMAGE_NAME: ${{ steps.set_docker_image_name.outputs.IMAGE_NAME }} | |
steps: | |
- name: 🕒 Set Timestamp | |
id: set_timestamp | |
run: echo "TIMESTAMP=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT | |
build: | |
needs: set | |
environment: Deploy-Prod | |
runs-on: ubuntu-latest | |
steps: | |
- name: 📂 Checkout Repository | |
uses: actions/checkout@v3 | |
- name: 🏗️ Setup JDK 21 | |
uses: actions/setup-java@v3 | |
with: | |
java-version: '21' | |
distribution: 'adopt' | |
- name: 🎖️ Grant Execute Permission | |
run: chmod +x gradlew | |
- name: ⚙️ Create application-prod.yml | |
run: | | |
touch ./src/main/resources/application-prod.yml | |
echo "${{ secrets.APPLICATION_PROD_YML }}" > ./src/main/resources/application-prod.yml | |
shell: bash | |
- name: 🔨 Build Gradle | |
run: ./gradlew build --no-daemon | |
- name: 🪂 Upload Artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
path: build/libs/groom-0.0.1-SNAPSHOT.jar | |
retention-days: 1 | |
push: | |
needs: build | |
runs-on: ubuntu-latest | |
environment: Deploy-Prod | |
steps: | |
- name: 📥 Load Outputs | |
run: echo "TIMESTAMP=${{ needs.set.outputs.TIMESTAMP }}" | |
- name: 🏷️ Set Docker Image Name | |
run: echo "IMAGE_NAME=${{ secrets.DOCKER_USERNAME }}/groom-server" >> $GITHUB_ENV | |
- name: 📂 Checkout Repository | |
uses: actions/checkout@v3 | |
- name: 📦 Download Artifact | |
uses: actions/download-artifact@v4 | |
with: | |
path: build/libs/ | |
- name: 🐋 Docker Login | |
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin | |
- name: 📦 Build Docker Image | |
run: docker build -t ${IMAGE_NAME}:latest -t ${IMAGE_NAME}:${TIMESTAMP} . | |
- name: 🚀 Push Docker Image | |
run: | | |
docker push ${IMAGE_NAME}:latest | |
docker push ${IMAGE_NAME}:${TIMESTAMP} | |
deploy: | |
needs: push | |
runs-on: ubuntu-latest | |
environment: Deploy-Prod | |
steps: | |
- name: 🔑 Setup SSH | |
run: | | |
mkdir -p ~/.ssh | |
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa | |
chmod 600 ~/.ssh/id_rsa | |
ssh-agent bash -c "ssh-add ~/.ssh/id_rsa" | |
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts | |
- name: 🚀 Deploy to Production | |
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'EOF' | |
set -e | |
CONTAINER_NAME=groom-server | |
IMAGE_NAME="${{ secrets.DOCKER_USERNAME }}/groom-server" | |
echo "🛑 Stop and Remove Existing Container" | |
docker stop $CONTAINER_NAME || true | |
docker rm $CONTAINER_NAME || true | |
echo "📥 Pull Latest Image" | |
docker pull ${IMAGE_NAME}:latest | |
echo "🚀 Run New Container" | |
docker run -d --name $CONTAINER_NAME -p 8080:8080 \ | |
-e TZ=Asia/Seoul \ | |
-e SPRING_PROFILES_ACTIVE=prod \ | |
${IMAGE_NAME}:latest | |
echo "⌛ Waiting for Container to Start" | |
sleep 10 | |
EOF | |
test: | |
needs: deploy | |
runs-on: ubuntu-latest | |
environment: Deploy-Prod | |
steps: | |
- name: 📥 Load Outputs | |
run: echo "TIMESTAMP=${{ needs.set.outputs.TIMESTAMP }}" | |
- name: 🏷️ Set Docker Image Name | |
run: echo "IMAGE_NAME=${{ secrets.DOCKER_USERNAME }}/groom-server" >> $GITHUB_ENV | |
- name: 🔐 Setup SSH | |
run: | | |
mkdir -p ~/.ssh | |
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa | |
chmod 600 ~/.ssh/id_rsa | |
ssh-agent bash -c "ssh-add ~/.ssh/id_rsa" | |
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts | |
- name: 🫀 Check Application Health & Rollback if needed | |
run: | | |
ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'EOF' | |
set -e | |
CONTAINER_NAME=groom-server | |
echo "🩺 Checking Application Health..." | |
RESPONSE=\$(curl -s https://${{ secrets.PROD_SERVER_URL }}/) | |
if [ "\$RESPONSE" = "OK" ]; then | |
echo "✅ Deployment Successful!" | |
exit 0 | |
else | |
echo "❌ Deployment Failed! Rolling back..." | |
docker stop \$CONTAINER_NAME || true | |
docker rm \$CONTAINER_NAME || true | |
PREVIOUS_IMAGE=\$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "$IMAGE_NAME" | grep -v "latest" | sort -r | sed -n '2p') | |
if [ -z "\$PREVIOUS_IMAGE" ]; then | |
echo "⚠ No previous image found! Manual rollback required." | |
echo "::warning::⚠ Deployment failed, and no previous image found. Manual rollback required!" | |
exit 1 | |
fi | |
echo "🔄 Rolling back to previous image: \$PREVIOUS_IMAGE" | |
docker run -d --name \$CONTAINER_NAME -p 8080:8080 \ | |
-e TZ=Asia/Seoul \ | |
-e SPRING_PROFILES_ACTIVE=prod \ | |
\$PREVIOUS_IMAGE | |
echo "✅ Rollback completed!" | |
echo "::warning::⚠ Deployment failed, rollback was executed. Please check the logs for details!" | |
exit 1 | |
fi | |
EOF | |
- name: 📢 Post Warning in Summary | |
if: failure() | |
run: | | |
echo "⚠️ **Deployment failed, rollback was executed. Please check the logs for details!**" >> $GITHUB_STEP_SUMMARY |