diff --git a/.github/workflows/deploy-to-dev-ec2.yml b/.github/workflows/deploy-to-dev-ec2.yml index 0a96fb08..7419faee 100644 --- a/.github/workflows/deploy-to-dev-ec2.yml +++ b/.github/workflows/deploy-to-dev-ec2.yml @@ -37,6 +37,106 @@ jobs: target: ~/nadab/ overwrite: true + - name: πŸ” Prepare deployment secrets + env: + INSIGHT_PROMPT_B64: ${{ secrets.INSIGHT_PROMPT_B64 }} + INSIGHT_WITH_IMAGE_PROMPT_B64: ${{ secrets.INSIGHT_WITH_IMAGE_PROMPT_B64 }} + WEEKLY_PROMPT_B64: ${{ secrets.WEEKLY_PROMPT_B64 }} + MONTHLY_V1_PROMPT_B64: ${{ secrets.MONTHLY_V1_PROMPT_B64 }} + MONTHLY_V2_BASELINE_PROMPT_B64: ${{ secrets.MONTHLY_V2_BASELINE_PROMPT_B64 }} + MONTHLY_V2_COMPARISON_PROMPT_B64: ${{ secrets.MONTHLY_V2_COMPARISON_PROMPT_B64 }} + EVIDENCE_CARD_PROMPT_B64: ${{ secrets.EVIDENCE_CARD_PROMPT_B64 }} + PATTERN_EXTRACT_PROMPT_B64: ${{ secrets.PATTERN_EXTRACT_PROMPT_B64 }} + TYPE_SELECT_PROMPT_B64: ${{ secrets.TYPE_SELECT_PROMPT_B64 }} + TYPE_PROMPT_B64: ${{ secrets.TYPE_PROMPT_B64 }} + REPAIR_TYPE_PROMPT_B64: ${{ secrets.REPAIR_TYPE_PROMPT_B64 }} + ASK_CHAT_ANSWER_SYSTEM_PROMPT_B64: ${{ secrets.ASK_CHAT_ANSWER_SYSTEM_PROMPT_B64 }} + ASK_CHAT_ANSWER_USER_PROMPT_B64: ${{ secrets.ASK_CHAT_ANSWER_USER_PROMPT_B64 }} + NOTIFICATION_MESSAGES_B64: ${{ secrets.NOTIFICATION_MESSAGES_B64 }} + DB_URL: ${{ secrets.DB_URL }} + DB_USERNAME: ${{ secrets.DB_USERNAME }} + DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + JWT_SECRET: ${{ secrets.JWT_SECRET }} + FRONTEND_DEV_URL: ${{ secrets.FRONTEND_DEV_URL }} + FRONTEND_DEV_URL_2: ${{ secrets.FRONTEND_DEV_URL_2 }} + FRONTEND_DEV_URL_3: ${{ secrets.FRONTEND_DEV_URL_3 }} + SWAGGER_SERVER_URL: ${{ secrets.SWAGGER_SERVER_URL }} + SWAGGER_ORIGIN_PUBLIC_IP: ${{ secrets.SWAGGER_ORIGIN_PUBLIC_IP }} + SWAGGER_ORIGIN_DOMAIN: ${{ secrets.SWAGGER_ORIGIN_DOMAIN }} + NAVER_CLIENT_ID: ${{ secrets.NAVER_CLIENT_ID }} + NAVER_CLIENT_SECRET: ${{ secrets.NAVER_CLIENT_SECRET }} + NAVER_REDIRECT_URI: ${{ secrets.NAVER_REDIRECT_URI }} + GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} + GOOGLE_ANDROID_CLIENT_ID: ${{ secrets.GOOGLE_ANDROID_CLIENT_ID }} + GOOGLE_IOS_CLIENT_ID: ${{ secrets.GOOGLE_IOS_CLIENT_ID }} + GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} + GOOGLE_REDIRECT_URI: ${{ secrets.GOOGLE_REDIRECT_URI }} + KAKAO_CLIENT_ID: ${{ secrets.KAKAO_CLIENT_ID }} + KAKAO_CLIENT_SECRET: ${{ secrets.KAKAO_CLIENT_SECRET }} + KAKAO_REDIRECT_URI: ${{ secrets.KAKAO_REDIRECT_URI }} + KAKAO_APP_ID: ${{ secrets.KAKAO_APP_ID }} + APPLE_CLIENT_ID: ${{ secrets.APPLE_CLIENT_ID }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} + APPLE_PRIVATE_KEY: ${{ secrets.APPLE_PRIVATE_KEY }} + IAM_ACCESS_KEY: ${{ secrets.IAM_ACCESS_KEY }} + IAM_SECRET_KEY: ${{ secrets.IAM_SECRET_KEY }} + BUCKET_NAME: ${{ secrets.BUCKET_NAME }} + PROFILE_IMAGE_BASE_URL: ${{ secrets.PROFILE_IMAGE_BASE_URL }} + CLOUDFRONT_KEY_PAIR_ID: ${{ secrets.CLOUDFRONT_KEY_PAIR_ID }} + CLOUDFRONT_PRIVATE_KEY: ${{ secrets.CLOUDFRONT_PRIVATE_KEY }} + KMS_KEY_ID: ${{ secrets.KMS_KEY_ID }} + MAIL_ADDRESS: ${{ secrets.MAIL_ADDRESS }} + MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GOOGLE_GENAI_API_KEY: ${{ secrets.GOOGLE_GENAI_API_KEY }} + FIREBASE_ADMIN_KEY: ${{ secrets.FIREBASE_ADMIN_KEY }} + ADMIN_PAGE_PASSWORD: ${{ secrets.ADMIN_PAGE_PASSWORD }} + DEV_TEST_ACCOUNT_PASSWORD: ${{ secrets.DEV_TEST_ACCOUNT_PASSWORD }} + run: | + set -euo pipefail + umask 077 + mkdir -p deploy-secrets + + SECRET_NAMES=( + INSIGHT_PROMPT_B64 INSIGHT_WITH_IMAGE_PROMPT_B64 WEEKLY_PROMPT_B64 + MONTHLY_V1_PROMPT_B64 MONTHLY_V2_BASELINE_PROMPT_B64 MONTHLY_V2_COMPARISON_PROMPT_B64 + EVIDENCE_CARD_PROMPT_B64 PATTERN_EXTRACT_PROMPT_B64 TYPE_SELECT_PROMPT_B64 + TYPE_PROMPT_B64 REPAIR_TYPE_PROMPT_B64 ASK_CHAT_ANSWER_SYSTEM_PROMPT_B64 + ASK_CHAT_ANSWER_USER_PROMPT_B64 NOTIFICATION_MESSAGES_B64 + DB_URL DB_USERNAME DB_PASSWORD JWT_SECRET + FRONTEND_DEV_URL FRONTEND_DEV_URL_2 FRONTEND_DEV_URL_3 + SWAGGER_SERVER_URL SWAGGER_ORIGIN_PUBLIC_IP SWAGGER_ORIGIN_DOMAIN + NAVER_CLIENT_ID NAVER_CLIENT_SECRET NAVER_REDIRECT_URI + GOOGLE_CLIENT_ID GOOGLE_ANDROID_CLIENT_ID GOOGLE_IOS_CLIENT_ID + GOOGLE_CLIENT_SECRET GOOGLE_REDIRECT_URI + KAKAO_CLIENT_ID KAKAO_CLIENT_SECRET KAKAO_REDIRECT_URI KAKAO_APP_ID + APPLE_CLIENT_ID APPLE_TEAM_ID APPLE_KEY_ID APPLE_PRIVATE_KEY + IAM_ACCESS_KEY IAM_SECRET_KEY BUCKET_NAME PROFILE_IMAGE_BASE_URL + CLOUDFRONT_KEY_PAIR_ID CLOUDFRONT_PRIVATE_KEY KMS_KEY_ID + MAIL_ADDRESS MAIL_PASSWORD OPENAI_API_KEY ANTHROPIC_API_KEY + GOOGLE_GENAI_API_KEY FIREBASE_ADMIN_KEY ADMIN_PAGE_PASSWORD DEV_TEST_ACCOUNT_PASSWORD + ) + + for name in "${SECRET_NAMES[@]}"; do + printf '%s' "${!name}" | base64 -w 0 > "deploy-secrets/$name" + done + + # scp-action Docker μ»¨ν…Œμ΄λ„ˆκ°€ λ‹€λ₯Έ UID둜 νŒŒμΌμ„ 읽을 수 μžˆλ„λ‘ 전솑 μ§μ „μ—λ§Œ κΆŒν•œ ν—ˆμš© + chmod 755 deploy-secrets + chmod 644 deploy-secrets/* + + - name: πŸ“€ Upload deployment secrets to EC2 + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.EC2_HOST }} + username: ec2-user + key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} + source: ./deploy-secrets + target: ~/nadab/ + overwrite: true + - name: πŸš€ Restart App on EC2 uses: appleboy/ssh-action@v1 with: @@ -44,7 +144,7 @@ jobs: username: ec2-user key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} script: | - set -euxo pipefail + set -euo pipefail cd ~/nadab echo "πŸ”Ž ν˜„μž¬ μ„œλ²„ 디렉토리: $(pwd)" @@ -59,112 +159,89 @@ jobs: JAR_NAME=$(basename "$JAR_PATH") echo "πŸ“Œ 배포할 JAR 선택됨: $JAR_PATH" - - # 싀행쀑인 ν”„λ‘œμ„ΈμŠ€ μ’…λ£Œ + + SECRETS_DIR="deploy-secrets" + [ -d "$SECRETS_DIR" ] || { echo "❌ 배포 Secret 디렉토리가 μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€"; exit 1; } + chmod 700 "$SECRETS_DIR" + chmod 600 "$SECRETS_DIR"/* + + cleanup_secrets() { + rm -f -- "$SECRETS_DIR"/* + rmdir -- "$SECRETS_DIR" + } + trap cleanup_secrets EXIT + + load_secret() { + local name="$1" + local path="$SECRETS_DIR/$name" + [ -f "$path" ] || { echo "❌ ν•„μˆ˜ Secret 파일 λˆ„λ½: $name"; exit 1; } + + local value + value="$(base64 -d "$path")" + printf -v "$name" '%s' "$value" + export "$name" + } + + load_prompt() { + local name="$1" + local path="$SECRETS_DIR/${name}_B64" + [ -f "$path" ] || { echo "❌ ν•„μˆ˜ ν”„λ‘¬ν”„νŠΈ 파일 λˆ„λ½: ${name}_B64"; exit 1; } + + local value + value="$(base64 -d "$path" | base64 -d | tr -d '\r')" + printf -v "$name" '%s' "$value" + export "$name" + } + + PROMPT_NAMES=( + INSIGHT_PROMPT INSIGHT_WITH_IMAGE_PROMPT WEEKLY_PROMPT + MONTHLY_V1_PROMPT MONTHLY_V2_BASELINE_PROMPT MONTHLY_V2_COMPARISON_PROMPT + EVIDENCE_CARD_PROMPT PATTERN_EXTRACT_PROMPT TYPE_SELECT_PROMPT TYPE_PROMPT + REPAIR_TYPE_PROMPT ASK_CHAT_ANSWER_SYSTEM_PROMPT ASK_CHAT_ANSWER_USER_PROMPT + NOTIFICATION_MESSAGES + ) + + SECRET_NAMES=( + DB_URL DB_USERNAME DB_PASSWORD JWT_SECRET + FRONTEND_DEV_URL FRONTEND_DEV_URL_2 FRONTEND_DEV_URL_3 + SWAGGER_SERVER_URL SWAGGER_ORIGIN_PUBLIC_IP SWAGGER_ORIGIN_DOMAIN + NAVER_CLIENT_ID NAVER_CLIENT_SECRET NAVER_REDIRECT_URI + GOOGLE_CLIENT_ID GOOGLE_ANDROID_CLIENT_ID GOOGLE_IOS_CLIENT_ID + GOOGLE_CLIENT_SECRET GOOGLE_REDIRECT_URI + KAKAO_CLIENT_ID KAKAO_CLIENT_SECRET KAKAO_REDIRECT_URI KAKAO_APP_ID + APPLE_CLIENT_ID APPLE_TEAM_ID APPLE_KEY_ID APPLE_PRIVATE_KEY + IAM_ACCESS_KEY IAM_SECRET_KEY BUCKET_NAME PROFILE_IMAGE_BASE_URL + CLOUDFRONT_KEY_PAIR_ID CLOUDFRONT_PRIVATE_KEY KMS_KEY_ID + MAIL_ADDRESS MAIL_PASSWORD OPENAI_API_KEY ANTHROPIC_API_KEY + GOOGLE_GENAI_API_KEY FIREBASE_ADMIN_KEY ADMIN_PAGE_PASSWORD DEV_TEST_ACCOUNT_PASSWORD + ) + + for name in "${PROMPT_NAMES[@]}"; do + load_prompt "$name" + done + + for name in "${SECRET_NAMES[@]}"; do + load_secret "$name" + done + + cleanup_secrets + trap - EXIT + + # Secret 볡원이 μ™„λ£Œλœ ν›„μ—λ§Œ κΈ°μ‘΄ ν”„λ‘œμ„ΈμŠ€ μ’…λ£Œ echo "πŸ›‘ κΈ°μ‘΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ’…λ£Œ 쀑..." pkill -f "$JAR_NAME" || true - - echo "πŸš€ μƒˆ 버전 μ‹€ν–‰..." - - # βœ… base64둜 μΈμ½”λ”©λœ ν”„λ‘¬ν”„νŠΈλ₯Ό ν™˜κ²½ λ³€μˆ˜μ— λ””μ½”λ”©ν•΄μ„œ λ„£κΈ° - INSIGHT_PROMPT_B64="${{ secrets.INSIGHT_PROMPT_B64 }}" - INSIGHT_PROMPT="$(printf '%s' "$INSIGHT_PROMPT_B64" | base64 -d | tr -d '\r')" - export INSIGHT_PROMPT - - INSIGHT_WITH_IMAGE_PROMPT_B64="${{ secrets.INSIGHT_WITH_IMAGE_PROMPT_B64 }}" - INSIGHT_WITH_IMAGE_PROMPT="$(printf '%s' "$INSIGHT_WITH_IMAGE_PROMPT_B64" | base64 -d | tr -d '\r')" - export INSIGHT_WITH_IMAGE_PROMPT - - WEEKLY_PROMPT_B64="${{ secrets.WEEKLY_PROMPT_B64 }}" - WEEKLY_PROMPT="$(printf '%s' "$WEEKLY_PROMPT_B64" | base64 -d | tr -d '\r')" - export WEEKLY_PROMPT - - MONTHLY_V1_PROMPT_B64="${{ secrets.MONTHLY_V1_PROMPT_B64 }}" - MONTHLY_V1_PROMPT="$(printf '%s' "$MONTHLY_V1_PROMPT_B64" | base64 -d | tr -d '\r')" - export MONTHLY_V1_PROMPT - - MONTHLY_V2_BASELINE_PROMPT_B64="${{ secrets.MONTHLY_V2_BASELINE_PROMPT_B64 }}" - MONTHLY_V2_BASELINE_PROMPT="$(printf '%s' "$MONTHLY_V2_BASELINE_PROMPT_B64" | base64 -d | tr -d '\r')" - export MONTHLY_V2_BASELINE_PROMPT - MONTHLY_V2_COMPARISON_PROMPT_B64="${{ secrets.MONTHLY_V2_COMPARISON_PROMPT_B64 }}" - MONTHLY_V2_COMPARISON_PROMPT="$(printf '%s' "$MONTHLY_V2_COMPARISON_PROMPT_B64" | base64 -d | tr -d '\r')" - export MONTHLY_V2_COMPARISON_PROMPT - - EVIDENCE_CARD_PROMPT_B64="${{ secrets.EVIDENCE_CARD_PROMPT_B64 }}" - EVIDENCE_CARD_PROMPT="$(printf '%s' "$EVIDENCE_CARD_PROMPT_B64" | base64 -d | tr -d '\r')" - export EVIDENCE_CARD_PROMPT - - PATTERN_EXTRACT_PROMPT_B64="${{ secrets.PATTERN_EXTRACT_PROMPT_B64 }}" - PATTERN_EXTRACT_PROMPT="$(printf '%s' "$PATTERN_EXTRACT_PROMPT_B64" | base64 -d | tr -d '\r')" - export PATTERN_EXTRACT_PROMPT - - TYPE_SELECT_PROMPT_B64="${{ secrets.TYPE_SELECT_PROMPT_B64 }}" - TYPE_SELECT_PROMPT="$(printf '%s' "$TYPE_SELECT_PROMPT_B64" | base64 -d | tr -d '\r')" - export TYPE_SELECT_PROMPT - - TYPE_PROMPT_B64="${{ secrets.TYPE_PROMPT_B64 }}" - TYPE_PROMPT="$(printf '%s' "$TYPE_PROMPT_B64" | base64 -d | tr -d '\r')" - export TYPE_PROMPT - - REPAIR_TYPE_PROMPT_B64="${{ secrets.REPAIR_TYPE_PROMPT_B64 }}" - REPAIR_TYPE_PROMPT="$(printf '%s' "$REPAIR_TYPE_PROMPT_B64" | base64 -d | tr -d '\r')" - export REPAIR_TYPE_PROMPT - - ASK_CHAT_ANSWER_SYSTEM_PROMPT_B64="${{ secrets.ASK_CHAT_ANSWER_SYSTEM_PROMPT_B64 }}" - ASK_CHAT_ANSWER_SYSTEM_PROMPT="$(printf '%s' "$ASK_CHAT_ANSWER_SYSTEM_PROMPT_B64" | base64 -d | tr -d '\r')" - export ASK_CHAT_ANSWER_SYSTEM_PROMPT - - ASK_CHAT_ANSWER_USER_PROMPT_B64="${{ secrets.ASK_CHAT_ANSWER_USER_PROMPT_B64 }}" - ASK_CHAT_ANSWER_USER_PROMPT="$(printf '%s' "$ASK_CHAT_ANSWER_USER_PROMPT_B64" | base64 -d | tr -d '\r')" - export ASK_CHAT_ANSWER_USER_PROMPT - - NOTIFICATION_MESSAGES_B64="${{ secrets.NOTIFICATION_MESSAGES_B64 }}" - NOTIFICATION_MESSAGES="$(printf '%s' "$NOTIFICATION_MESSAGES_B64" | base64 -d | tr -d '\r')" - export NOTIFICATION_MESSAGES - - DB_URL="${{ secrets.DB_URL }}" \ - DB_USERNAME="${{ secrets.DB_USERNAME }}" \ - DB_PASSWORD="${{ secrets.DB_PASSWORD }}" \ - JWT_SECRET="${{ secrets.JWT_SECRET }}" \ - FRONTEND_DEV_URL="${{ secrets.FRONTEND_DEV_URL }}" \ - FRONTEND_DEV_URL_2="${{ secrets.FRONTEND_DEV_URL_2 }}" \ - FRONTEND_DEV_URL_3="${{ secrets.FRONTEND_DEV_URL_3 }}" \ - SWAGGER_SERVER_URL="${{ secrets.SWAGGER_SERVER_URL }}" \ - SWAGGER_ORIGIN_PUBLIC_IP="${{ secrets.SWAGGER_ORIGIN_PUBLIC_IP }}" \ - SWAGGER_ORIGIN_DOMAIN="${{ secrets.SWAGGER_ORIGIN_DOMAIN }}" \ - NAVER_CLIENT_ID="${{ secrets.NAVER_CLIENT_ID }}" \ - NAVER_CLIENT_SECRET="${{ secrets.NAVER_CLIENT_SECRET }}" \ - NAVER_REDIRECT_URI="${{ secrets.NAVER_REDIRECT_URI }}" \ - GOOGLE_CLIENT_ID="${{ secrets.GOOGLE_CLIENT_ID }}" \ - GOOGLE_ANDROID_CLIENT_ID="${{ secrets.GOOGLE_ANDROID_CLIENT_ID }}" \ - GOOGLE_IOS_CLIENT_ID="${{ secrets.GOOGLE_IOS_CLIENT_ID }}" \ - GOOGLE_CLIENT_SECRET="${{ secrets.GOOGLE_CLIENT_SECRET }}" \ - GOOGLE_REDIRECT_URI="${{ secrets.GOOGLE_REDIRECT_URI }}" \ - KAKAO_CLIENT_ID="${{ secrets.KAKAO_CLIENT_ID }}" \ - KAKAO_CLIENT_SECRET="${{ secrets.KAKAO_CLIENT_SECRET }}" \ - KAKAO_REDIRECT_URI="${{ secrets.KAKAO_REDIRECT_URI }}" \ - KAKAO_APP_ID="${{ secrets.KAKAO_APP_ID }}" \ - APPLE_CLIENT_ID="${{ secrets.APPLE_CLIENT_ID }}" \ - APPLE_TEAM_ID="${{ secrets.APPLE_TEAM_ID }}" \ - APPLE_KEY_ID="${{ secrets.APPLE_KEY_ID }}" \ - APPLE_PRIVATE_KEY="${{ secrets.APPLE_PRIVATE_KEY }}" \ - IAM_ACCESS_KEY="${{ secrets.IAM_ACCESS_KEY }}" \ - IAM_SECRET_KEY="${{ secrets.IAM_SECRET_KEY }}" \ - BUCKET_NAME="${{ secrets.BUCKET_NAME }}" \ - PROFILE_IMAGE_BASE_URL="${{ secrets.PROFILE_IMAGE_BASE_URL }}" \ - CLOUDFRONT_KEY_PAIR_ID="${{ secrets.CLOUDFRONT_KEY_PAIR_ID }}" \ - CLOUDFRONT_PRIVATE_KEY="${{ secrets.CLOUDFRONT_PRIVATE_KEY }}" \ - KMS_KEY_ID="${{ secrets.KMS_KEY_ID }}" \ - MAIL_ADDRESS="${{ secrets.MAIL_ADDRESS }}" \ - MAIL_PASSWORD="${{ secrets.MAIL_PASSWORD }}" \ - OPENAI_API_KEY="${{ secrets.OPENAI_API_KEY }}" \ - ANTHROPIC_API_KEY="${{ secrets.ANTHROPIC_API_KEY }}" \ - GOOGLE_GENAI_API_KEY="${{ secrets.GOOGLE_GENAI_API_KEY }}" \ - FIREBASE_ADMIN_KEY="${{ secrets.FIREBASE_ADMIN_KEY }}" \ - ADMIN_PAGE_PASSWORD='${{ secrets.ADMIN_PAGE_PASSWORD }}' \ - DEV_TEST_ACCOUNT_PASSWORD="${{ secrets.DEV_TEST_ACCOUNT_PASSWORD }}" \ + echo "πŸš€ μƒˆ 버전 μ‹€ν–‰..." nohup java -jar "$JAR_PATH" \ --spring.profiles.active=dev > app.log 2>&1 & echo "βœ… 배포 μ™„λ£Œ!" + + - name: 🧹 Cleanup deployment secrets + if: always() + run: | + set -euo pipefail + if [ -d deploy-secrets ]; then + rm -f -- deploy-secrets/* + rmdir -- deploy-secrets + fi