refactor(deploy): TaxBaik 성공 사례(run 458) 기반 단일 빌드/배포 파이프라인 개편 및 텔레그램 연동 강화
Quant Engine CI/CD Pipeline / validate-ui-and-storage (push) Has been skipped
Snapshot Admin Deployment / build-and-deploy (push) Failing after 44s
Quant Engine CI/CD Pipeline / validate-core (push) Failing after 7s
Deploy to Production / Build & Deploy to Production (push) Failing after 1m33s
Quant Engine CI/CD Pipeline / validate-ui-and-storage (push) Has been skipped
Snapshot Admin Deployment / build-and-deploy (push) Failing after 44s
Quant Engine CI/CD Pipeline / validate-core (push) Failing after 7s
Deploy to Production / Build & Deploy to Production (push) Failing after 1m33s
This commit is contained in:
+100
-318
@@ -7,18 +7,16 @@ on:
|
||||
|
||||
env:
|
||||
DEPLOY_HOST: 172.17.0.1
|
||||
# NOTE: Gitea와 운영서버가 같은 호스트에 있음 (hz-prod-01)
|
||||
# 구조: 공인 IP 178.104.200.7/quant → Nginx reverse proxy → localhost:5000 (quantengine)
|
||||
# 배포: .NET DLL을 /home/kjh2064/quantengine_active에 배포
|
||||
# Nginx 설정: /etc/nginx/sites-available/gitea-ip.conf (이미 구성됨)
|
||||
DEPLOY_USER: kjh2064
|
||||
DEPLOY_PATH: /home/kjh2064/quantengine_active
|
||||
SERVICE_NAME: quantengine
|
||||
DOTNET_VERSION: '10.0.x'
|
||||
TELEGRAM_BOT_TOKEN_DEFAULT: "8734507814:AAFyacLMai8GB4K-hQ_Nd3t3D01A-h1ZdV0"
|
||||
TELEGRAM_CHAT_ID_DEFAULT: "-5460205872"
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
name: Build Release Package
|
||||
build-and-deploy:
|
||||
name: Build & Deploy to Production
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -42,12 +40,19 @@ jobs:
|
||||
|
||||
- name: "[GATE] Run Core Validations"
|
||||
run: |
|
||||
# CI 게이트: 핵심 검증 먼저 실행
|
||||
echo "🔐 Running critical CI validations..."
|
||||
python3 tools/validate_no_direct_api_trading_v1.py || exit 1
|
||||
python3 tools/validate_specs.py || exit 1
|
||||
echo "✅ All critical validations passed"
|
||||
|
||||
- name: Ensure Temp Directory and Mock Packet
|
||||
run: |
|
||||
mkdir -p Temp
|
||||
# 빈 패킷 객체를 생성하여 dotnet test/run 시 IO Exception 방어
|
||||
if [ ! -f Temp/final_decision_packet_active.json ]; then
|
||||
echo '{"active_decision": "PASS", "details": "CI dummy packet"}' > Temp/final_decision_packet_active.json
|
||||
fi
|
||||
|
||||
- name: Restore Dependencies
|
||||
run: dotnet restore src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
|
||||
|
||||
@@ -64,7 +69,6 @@ jobs:
|
||||
dotnet test tests/unit \
|
||||
-c Release \
|
||||
--no-build \
|
||||
--logger "trx;LogFileName=test-results.trx" \
|
||||
|| echo "⚠️ Some tests failed (non-blocking for web service)"
|
||||
fi
|
||||
|
||||
@@ -75,329 +79,107 @@ jobs:
|
||||
--no-build \
|
||||
-o ./publish-output
|
||||
|
||||
echo "📦 Package size:"
|
||||
du -sh ./publish-output
|
||||
|
||||
- name: Create Deployment Archive
|
||||
run: |
|
||||
cd publish-output
|
||||
tar -czf ../quant-engine-release-${{ github.run_number }}.tar.gz .
|
||||
cd ..
|
||||
ls -lh quant-engine-release-${{ github.run_number }}.tar.gz
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: quant-engine-release
|
||||
path: quant-engine-release-${{ github.run_number }}.tar.gz
|
||||
retention-days: 30
|
||||
|
||||
deploy-to-prod:
|
||||
name: Deploy to Production Server
|
||||
needs: build-and-test
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: quant-engine-release
|
||||
|
||||
- name: Setup SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||
# SSH_PRIVATE_KEY가 평문 PEM이든 base64든 유연하게 처리
|
||||
if echo "${{ secrets.SSH_PRIVATE_KEY }}" | grep -q "BEGIN"; then
|
||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||
else
|
||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" | base64 -d > ~/.ssh/id_ed25519 || echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||
fi
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
ssh-keyscan -H ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||
|
||||
- name: Create Backup
|
||||
- name: Package Artifact
|
||||
run: |
|
||||
echo "📦 Creating backup of current deployment..."
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
||||
set -e
|
||||
BACKUP_DIR="/home/kjh2064/quantengine_backup"
|
||||
BACKUP_NAME="quantengine_$(date +%Y%m%d_%H%M%S)"
|
||||
tar -czf quant_engine_deploy.tgz -C ./publish-output .
|
||||
echo "✓ Package size: $(du -sh quant_engine_deploy.tgz | cut -f1)"
|
||||
|
||||
# Create backup without stopping the service (minimize downtime)
|
||||
mkdir -p $BACKUP_DIR
|
||||
if [ -d ${{ env.DEPLOY_PATH }} ]; then
|
||||
cp -r ${{ env.DEPLOY_PATH }} "$BACKUP_DIR/$BACKUP_NAME"
|
||||
echo "✅ Backup created: $BACKUP_DIR/$BACKUP_NAME"
|
||||
- name: Deploy & Verify on Server
|
||||
run: |
|
||||
set -e
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
COMMIT=$(git rev-parse --short HEAD)
|
||||
DEPLOY_HOST="${{ env.DEPLOY_HOST }}"
|
||||
DEPLOY_USER="${{ env.DEPLOY_USER }}"
|
||||
|
||||
# 텔레그램 설정 바인딩 (Secret에 없을 경우 기본값 백업 사용)
|
||||
TELEGRAM_BOT_TOKEN="${{ secrets.TELEGRAM_BOT_TOKEN }}"
|
||||
[ -z "$TELEGRAM_BOT_TOKEN" ] && TELEGRAM_BOT_TOKEN="${{ env.TELEGRAM_BOT_TOKEN_DEFAULT }}"
|
||||
TELEGRAM_CHAT_ID="${{ secrets.TELEGRAM_CHAT_ID }}"
|
||||
[ -z "$TELEGRAM_CHAT_ID" ] && TELEGRAM_CHAT_ID="${{ env.TELEGRAM_CHAT_ID_DEFAULT }}"
|
||||
|
||||
# Keep only last 5 backups
|
||||
BACKUP_COUNT=$(ls -1 $BACKUP_DIR | wc -l)
|
||||
if [ "$BACKUP_COUNT" -gt 5 ]; then
|
||||
OLD_BACKUPS=$(ls -1t $BACKUP_DIR | tail -n +6)
|
||||
for backup in $OLD_BACKUPS; do
|
||||
rm -rf "$BACKUP_DIR/$backup"
|
||||
done
|
||||
echo "🧹 Old backups cleaned"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ No existing deployment found"
|
||||
send_telegram() {
|
||||
local text="$1"
|
||||
curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||
-d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||
--data-urlencode "text=${text}" \
|
||||
-d "parse_mode=HTML" >/dev/null || true
|
||||
}
|
||||
|
||||
notify_failure() {
|
||||
local exit_code=$?
|
||||
send_telegram "❌ <b>QuantEngine 배포 실패</b>
|
||||
|
||||
커밋: <code>${COMMIT}</code>
|
||||
시간: <code>${TIMESTAMP}</code>
|
||||
단계: deploy-to-prod (SSH Execution)"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
trap notify_failure ERR
|
||||
|
||||
echo "=== Deploying QuantEngine $COMMIT ($TIMESTAMP) ==="
|
||||
|
||||
# 1. 아티팩트 복사
|
||||
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 \
|
||||
quant_engine_deploy.tgz "$DEPLOY_USER@$DEPLOY_HOST:/tmp/quantengine_${TIMESTAMP}.tgz"
|
||||
|
||||
# 2. 원격 배포 명령어 통합 (SSH 1회 연결)
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 \
|
||||
-o ServerAliveInterval=10 \
|
||||
"$DEPLOY_USER@$DEPLOY_HOST" bash << REMOTE
|
||||
set -e
|
||||
DEPLOY_HOME="/home/kjh2064"
|
||||
DEPLOY_DIR="\$DEPLOY_HOME/deployments/quantengine_${TIMESTAMP}"
|
||||
|
||||
echo "--- [1/4] 압축 해제 ---"
|
||||
mkdir -p "\$DEPLOY_DIR"
|
||||
tar -xzf "/tmp/quantengine_${TIMESTAMP}.tgz" -C "\$DEPLOY_DIR"
|
||||
rm -f "/tmp/quantengine_${TIMESTAMP}.tgz"
|
||||
|
||||
echo "--- [2/4] 심볼릭 링크 전환 ---"
|
||||
ln -sfn "\$DEPLOY_DIR" "${{ env.DEPLOY_PATH }}"
|
||||
|
||||
echo "--- [3/4] 서비스 재시작 ---"
|
||||
sudo /usr/bin/systemctl restart ${{ env.SERVICE_NAME }}
|
||||
|
||||
echo "--- [4/4] 헬스 체크 ---"
|
||||
ATTEMPTS=20
|
||||
for i in \\\$(seq 1 \\\$ATTEMPTS); do
|
||||
STATUS=\\\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5000/ 2>/dev/null || echo "000")
|
||||
if [ "\\\$STATUS" = "200" ]; then
|
||||
echo "✓ 헬스체크 성공 (시도 \\\$i/\\\$ATTEMPTS, HTTP 200)"
|
||||
# 구 배포 폴더 정리 (최근 5개만 보존)
|
||||
ls -1dt \$DEPLOY_HOME/deployments/quantengine_* 2>/dev/null | tail -n +6 | xargs rm -rf 2>/dev/null || true
|
||||
exit 0
|
||||
fi
|
||||
EOF
|
||||
|
||||
- name: Deploy Package
|
||||
run: |
|
||||
echo "📤 Deploying package to production..."
|
||||
|
||||
ARCHIVE_NAME=$(ls -1 quant-engine-release-*.tar.gz | head -1)
|
||||
|
||||
# Create temporary directory on remote
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
|
||||
"mkdir -p /tmp/quant-deploy && chmod 777 /tmp/quant-deploy"
|
||||
|
||||
# Transfer archive
|
||||
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 "$ARCHIVE_NAME" \
|
||||
${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:/tmp/quant-deploy/
|
||||
|
||||
echo "✅ Package transferred"
|
||||
|
||||
- name: Extract and Install
|
||||
run: |
|
||||
echo "📦 Extracting and installing..."
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
||||
set -e
|
||||
|
||||
DEPLOY_PATH="${{ env.DEPLOY_PATH }}"
|
||||
ARCHIVE_NAME=$(ls -1 /tmp/quant-deploy/quant-engine-release-*.tar.gz | head -1)
|
||||
|
||||
# Create deployment directory
|
||||
mkdir -p "$DEPLOY_PATH"
|
||||
|
||||
# Extract new package
|
||||
tar -xzf "$ARCHIVE_NAME" -C "$DEPLOY_PATH"
|
||||
echo "✅ Package extracted to $DEPLOY_PATH"
|
||||
|
||||
# Verify key files
|
||||
if [ -f "$DEPLOY_PATH/QuantEngine.Web.dll" ]; then
|
||||
echo "✅ QuantEngine.Web.dll verified"
|
||||
else
|
||||
echo "❌ QuantEngine.Web.dll not found!"
|
||||
if [ "\\\$i" -eq "\\\$ATTEMPTS" ]; then
|
||||
echo "=== FATAL: 서비스가 헬스체크 응답을 하지 않음 ===" >&2
|
||||
systemctl is-active ${{ env.SERVICE_NAME }} >&2 || true
|
||||
journalctl -u ${{ env.SERVICE_NAME }} --no-pager -n 50 >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Cleanup temp
|
||||
rm -rf /tmp/quant-deploy
|
||||
EOF
|
||||
|
||||
- name: Restart Service
|
||||
run: |
|
||||
echo "🔄 Restarting quantengine service to apply changes (Downtime minimal)..."
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
||||
set -e
|
||||
|
||||
# Restart service
|
||||
echo "⏹️ Restarting quantengine service..."
|
||||
sudo systemctl restart ${{ env.SERVICE_NAME }}
|
||||
echo " 대기 중... (\\\$i/\\\$ATTEMPTS, HTTP \\\$STATUS)"
|
||||
sleep 3
|
||||
|
||||
# Check status
|
||||
if sudo systemctl is-active --quiet ${{ env.SERVICE_NAME }}; then
|
||||
echo "✅ ${{ env.SERVICE_NAME }} restarted successfully"
|
||||
sudo systemctl status ${{ env.SERVICE_NAME }} | head -5
|
||||
else
|
||||
echo "❌ ${{ env.SERVICE_NAME }} failed to start"
|
||||
sudo systemctl status ${{ env.SERVICE_NAME }}
|
||||
exit 1
|
||||
fi
|
||||
EOF
|
||||
|
||||
- name: Health Check
|
||||
run: |
|
||||
echo "🧪 Running health checks on remote host..."
|
||||
|
||||
# Wait for service to be ready (localhost:5000/quant/ through Kestrel inside remote host)
|
||||
for i in {1..30}; do
|
||||
HTTP_CODE=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
|
||||
'curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:5000/quant/' || echo "000")
|
||||
|
||||
if [ "$HTTP_CODE" = "200" ]; then
|
||||
echo "✅ Health check passed (HTTP $HTTP_CODE inside remote host)"
|
||||
break
|
||||
fi
|
||||
|
||||
echo "⏳ Waiting for service... (attempt $i/30, HTTP $HTTP_CODE)"
|
||||
sleep 2
|
||||
done
|
||||
REMOTE
|
||||
|
||||
if [ "$HTTP_CODE" != "200" ]; then
|
||||
echo "❌ Health check failed after 60 seconds"
|
||||
echo "Service logs:"
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
|
||||
"sudo journalctl -u ${{ env.SERVICE_NAME }} -n 20" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify Deployment
|
||||
run: |
|
||||
echo "📊 Verifying deployment..."
|
||||
|
||||
# Check MudBlazor is loaded (via public IP)
|
||||
PUBLIC_IP="178.104.200.7"
|
||||
MUDBLAZOR_CHECK=$(curl -s "http://$PUBLIC_IP/quant/" | grep -c "MudBlazor" || echo "0")
|
||||
|
||||
if [ "$MUDBLAZOR_CHECK" -gt "0" ]; then
|
||||
echo "✅ MudBlazor UI loaded successfully"
|
||||
else
|
||||
echo "⚠️ MudBlazor might not be loaded correctly"
|
||||
fi
|
||||
|
||||
# Get page title
|
||||
PAGE_TITLE=$(curl -s "http://$PUBLIC_IP/quant/" | grep -o "<title>.*</title>" | head -1)
|
||||
echo "📄 Page title: $PAGE_TITLE"
|
||||
|
||||
- name: Generate Deployment Report
|
||||
if: always()
|
||||
run: |
|
||||
cat > deployment-report.txt << EOF
|
||||
═══════════════════════════════════════════════════════
|
||||
Quant Engine v9 Deployment Report
|
||||
═══════════════════════════════════════════════════════
|
||||
|
||||
Deployment Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
||||
Run Number: ${{ github.run_number }}
|
||||
Commit: ${{ github.sha }}
|
||||
Branch: ${{ github.ref }}
|
||||
|
||||
🎯 Target Environment
|
||||
Server: hz-prod-01
|
||||
Internal IP: ${{ env.DEPLOY_HOST }}
|
||||
Public IP: 178.104.200.7
|
||||
Deploy Path: ${{ env.DEPLOY_PATH }}
|
||||
Service: ${{ env.SERVICE_NAME }}
|
||||
|
||||
📊 Deployment Status: COMPLETED
|
||||
|
||||
✅ Release Build: Successful
|
||||
✅ Package Created: 24MB+
|
||||
✅ Backup Created: /home/kjh2064/quantengine_backup/
|
||||
✅ Package Deployed: ${{ env.DEPLOY_PATH }}
|
||||
✅ Service Started: ${{ env.SERVICE_NAME }}
|
||||
✅ Health Check: PASS (localhost:5000)
|
||||
✅ MudBlazor UI: Verified via public IP
|
||||
|
||||
🌐 Access Information
|
||||
Public URL: http://178.104.200.7/quant/
|
||||
Service Port: 127.0.0.1:5000
|
||||
Nginx Config: /etc/nginx/sites-available/gitea-ip.conf
|
||||
|
||||
📝 Service Architecture
|
||||
- Nginx (reverse proxy) listens on port 80/443
|
||||
- /quant/ path → localhost:5000 (quantengine service)
|
||||
- quantengine runs as user kjh2064
|
||||
- WorkingDirectory: /home/kjh2064/quantengine_active
|
||||
|
||||
🔍 Monitoring & Logs
|
||||
- Service: sudo systemctl status ${{ env.SERVICE_NAME }}
|
||||
- Logs: sudo journalctl -u ${{ env.SERVICE_NAME }} -f
|
||||
- Nginx: sudo tail -f /var/log/nginx/error.log
|
||||
- Deployment Log: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
🔄 Rollback Command (if needed):
|
||||
ssh kjh2064@${{ env.DEPLOY_HOST }} 'LATEST=\$(ls -t /home/kjh2064/quantengine_backup | head -1); cp -r /home/kjh2064/quantengine_backup/\$LATEST/* /home/kjh2064/quantengine_active/ && sudo systemctl restart ${{ env.SERVICE_NAME }}'
|
||||
|
||||
═══════════════════════════════════════════════════════
|
||||
EOF
|
||||
cat deployment-report.txt
|
||||
|
||||
- name: Upload Deployment Report
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: deployment-report
|
||||
path: deployment-report.txt
|
||||
retention-days: 90
|
||||
|
||||
- name: Notify Telegram
|
||||
if: always()
|
||||
run: |
|
||||
STATUS=${{ job.status }}
|
||||
if [ "$STATUS" = "success" ]; then
|
||||
EMOJI="✅"
|
||||
TEXT="*Quant Engine v9 Deployment SUCCESS* $EMOJI%0A• Run: #${{ github.run_number }}%0A• Commit: ${{ github.sha }}%0A• Service: ${{ env.SERVICE_NAME }}%0A• URL: http://178.104.200.7/quant/"
|
||||
else
|
||||
EMOJI="❌"
|
||||
TEXT="*Quant Engine v9 Deployment FAILED* $EMOJI%0A• Run: #${{ github.run_number }}%0A• Commit: ${{ github.sha }}%0A• Service: ${{ env.SERVICE_NAME }}%0A• URL: http://178.104.200.7/quant/"
|
||||
fi
|
||||
|
||||
curl -s -X POST "https://api.telegram.org/bot8734507814:AAFyacLMai8GB4K-hQ_Nd3t3D01A-h1ZdV0/sendMessage" \
|
||||
-d "chat_id=-5460205872" \
|
||||
-d "text=$TEXT" \
|
||||
-d "parse_mode=Markdown"
|
||||
|
||||
post-deployment:
|
||||
name: Post-Deployment Checks
|
||||
needs: deploy-to-prod
|
||||
runs-on: ubuntu-latest
|
||||
if: success()
|
||||
|
||||
steps:
|
||||
- name: Performance Baseline
|
||||
run: |
|
||||
echo "📈 Collecting performance metrics via Public IP..."
|
||||
|
||||
# Page load time
|
||||
START=$(date +%s%N)
|
||||
curl -s http://178.104.200.7/quant/ > /dev/null
|
||||
END=$(date +%s%N)
|
||||
LOAD_TIME=$(( (END - START) / 1000000 ))
|
||||
|
||||
echo "⏱️ Page load time: ${LOAD_TIME}ms"
|
||||
|
||||
if [ $LOAD_TIME -lt 2000 ]; then
|
||||
echo "✅ Load time acceptable (< 2s)"
|
||||
else
|
||||
echo "⚠️ Load time slightly slow (> 2s), but acceptable"
|
||||
fi
|
||||
|
||||
- name: Create Deployment Checklist
|
||||
run: |
|
||||
cat > deployment-checklist.txt << 'EOF'
|
||||
✅ Quant Engine v9 Deployment Complete
|
||||
|
||||
Web Service:
|
||||
[✓] Release build successful (24MB)
|
||||
[✓] Deployed to: http://178.104.200.7/quant/
|
||||
[✓] nginx restarted
|
||||
[✓] Health check: HTTP 200 OK
|
||||
[✓] MudBlazor UI verified
|
||||
[✓] Page load time: < 2s
|
||||
|
||||
Backup & Recovery:
|
||||
[✓] Backup created: /var/www/quant_backup/
|
||||
[✓] 5 previous backups retained
|
||||
[✓] Rollback ready
|
||||
|
||||
Next Steps:
|
||||
[ ] Monitor nginx logs: ssh kjh2064@178.104.200.7 'sudo tail -f /var/log/nginx/error.log'
|
||||
[ ] Check dashboard: http://178.104.200.7/quant/
|
||||
[ ] Verify all components loaded
|
||||
[ ] Test responsive design (mobile/tablet)
|
||||
[ ] Monitor performance metrics
|
||||
|
||||
GAS Deployment (Manual):
|
||||
[ ] Deploy gas_data_feed.gs to Google Apps Script
|
||||
[ ] Deploy live_outcome_ledger.gs
|
||||
[ ] Test signal tracking
|
||||
|
||||
Documentation:
|
||||
[ ] DEPLOYMENT_GUIDE.md
|
||||
[ ] DEPLOYMENT_STEPS.md
|
||||
[ ] UI_COMPLETENESS_REPORT.md
|
||||
[ ] V9_HARDENING_IMPLEMENTATION_ROADMAP.md
|
||||
EOF
|
||||
cat deployment-checklist.txt
|
||||
|
||||
- name: Upload Checklist
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: post-deployment-checklist
|
||||
path: deployment-checklist.txt
|
||||
retention-days: 30
|
||||
echo "✓ 배포 완료: quantengine_${TIMESTAMP} @ $DEPLOY_HOST"
|
||||
send_telegram "✅ <b>QuantEngine 배포 완료</b>
|
||||
|
||||
커밋: <code>${COMMIT}</code>
|
||||
시간: <code>${TIMESTAMP}</code>
|
||||
대상: <code>${DEPLOY_HOST}</code>"
|
||||
|
||||
@@ -10,6 +10,12 @@ concurrency:
|
||||
group: snapshot-admin-deploy-main
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
DEPLOY_HOST: 178.104.200.7
|
||||
DEPLOY_USER: kjh2064
|
||||
TELEGRAM_BOT_TOKEN_DEFAULT: "8734507814:AAFyacLMai8GB4K-hQ_Nd3t3D01A-h1ZdV0"
|
||||
TELEGRAM_CHAT_ID_DEFAULT: "-5460205872"
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -33,38 +39,84 @@ jobs:
|
||||
echo "[deploy] compressing publish output"
|
||||
tar -czf quantengine.tar.gz -C ./publish .
|
||||
|
||||
- name: Deploy to Host via Local SSH
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
- name: Setup SSH
|
||||
run: |
|
||||
echo "[deploy] setting up SSH and deploying shadow copy"
|
||||
mkdir -p ~/.ssh
|
||||
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
||||
chmod 700 ~/.ssh
|
||||
if echo "${{ secrets.SSH_PRIVATE_KEY }}" | grep -q "BEGIN"; then
|
||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||
else
|
||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" | base64 -d > ~/.ssh/id_ed25519 || echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||
fi
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
ssh-keyscan -H ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||
|
||||
# Upload artifact and deploy script to host
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 kjh2064@178.104.200.7 "mkdir -p /home/kjh2064/tmp"
|
||||
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 quantengine.tar.gz kjh2064@178.104.200.7:/home/kjh2064/tmp/quantengine.tar.gz
|
||||
|
||||
# Execute hot deploy script
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 kjh2064@178.104.200.7 "chmod +x /home/kjh2064/tmp/deploy.sh 2>/dev/null || true"
|
||||
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 tools/deploy_quantengine.sh kjh2064@178.104.200.7:/home/kjh2064/tmp/deploy.sh
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 kjh2064@178.104.200.7 "chmod +x /home/kjh2064/tmp/deploy.sh && /home/kjh2064/tmp/deploy.sh"
|
||||
|
||||
- name: Verify Public Routes
|
||||
- name: Deploy & Verify on Server
|
||||
run: |
|
||||
set -e
|
||||
root_html=$(curl -s "http://178.104.200.7/quant/")
|
||||
ops_html=$(curl -s "http://178.104.200.7/quant/operations")
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
COMMIT=$(git rev-parse --short HEAD)
|
||||
DEPLOY_HOST="${{ env.DEPLOY_HOST }}"
|
||||
DEPLOY_USER="${{ env.DEPLOY_USER }}"
|
||||
|
||||
TELEGRAM_BOT_TOKEN="${{ secrets.TELEGRAM_BOT_TOKEN }}"
|
||||
[ -z "$TELEGRAM_BOT_TOKEN" ] && TELEGRAM_BOT_TOKEN="${{ env.TELEGRAM_BOT_TOKEN_DEFAULT }}"
|
||||
TELEGRAM_CHAT_ID="${{ secrets.TELEGRAM_CHAT_ID }}"
|
||||
[ -z "$TELEGRAM_CHAT_ID" ] && TELEGRAM_CHAT_ID="${{ env.TELEGRAM_CHAT_ID_DEFAULT }}"
|
||||
|
||||
send_telegram() {
|
||||
local text="$1"
|
||||
curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||
-d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||
--data-urlencode "text=${text}" \
|
||||
-d "parse_mode=HTML" >/dev/null || true
|
||||
}
|
||||
|
||||
notify_failure() {
|
||||
local exit_code=$?
|
||||
send_telegram "❌ <b>Snapshot Admin 배포 실패</b>
|
||||
|
||||
커밋: <code>${COMMIT}</code>
|
||||
시간: <code>${TIMESTAMP}</code>
|
||||
단계: snapshot_admin_deploy (Deploy Execution)"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
trap notify_failure ERR
|
||||
|
||||
echo "=== Deploying Snapshot Admin $COMMIT ($TIMESTAMP) ==="
|
||||
|
||||
# 1. 원격지 임시 폴더 생성 및 업로드
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 "$DEPLOY_USER@$DEPLOY_HOST" "mkdir -p /home/kjh2064/tmp"
|
||||
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 quantengine.tar.gz "$DEPLOY_USER@$DEPLOY_HOST:/home/kjh2064/tmp/quantengine.tar.gz"
|
||||
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 tools/deploy_quantengine.sh "$DEPLOY_USER@$DEPLOY_HOST:/home/kjh2064/tmp/deploy.sh"
|
||||
|
||||
# 2. 배포 스크립트 실행
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 "$DEPLOY_USER@$DEPLOY_HOST" "chmod +x /home/kjh2064/tmp/deploy.sh && /home/kjh2064/tmp/deploy.sh"
|
||||
|
||||
# 3. 배포 성공 검증
|
||||
echo "=== Verifying Public Routes ==="
|
||||
root_html=$(curl -sf "http://${DEPLOY_HOST}/quant/" 2>/dev/null || echo "")
|
||||
ops_html=$(curl -sf "http://${DEPLOY_HOST}/quant/operations" 2>/dev/null || echo "")
|
||||
|
||||
root_code=$(printf '%s' "$root_html" | grep -q "Quant Engine" && echo 200 || echo 500)
|
||||
ops_code=$(printf '%s' "$ops_html" | grep -q "Operational Report" && echo 200 || echo 500)
|
||||
|
||||
echo "/quant/ -> ${root_code}"
|
||||
echo "/quant/operations -> ${ops_code}"
|
||||
|
||||
if [ "$root_code" != "200" ]; then
|
||||
echo "Deployment content check failed for /quant/"
|
||||
echo "Deployment content check failed for /quant/" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$ops_code" != "200" ]; then
|
||||
echo "Deployment content check failed for /quant/operations"
|
||||
echo "Deployment content check failed for /quant/operations" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ 배포 완료: quantengine_${TIMESTAMP} @ $DEPLOY_HOST"
|
||||
send_telegram "✅ <b>Snapshot Admin 배포 완료</b>
|
||||
|
||||
커밋: <code>${COMMIT}</code>
|
||||
시간: <code>${TIMESTAMP}</code>
|
||||
대상: <code>${DEPLOY_HOST}</code>"
|
||||
|
||||
Reference in New Issue
Block a user