name: CI / CD Pipeline on: push: branches: [ main, develop ] pull_request: workflow_dispatch: jobs: # ============================================================ # JOB 1 : CI & Quality Gate # ============================================================ build: runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Checkout (SSH) run: | set -e # Exit on any error echo "Setting up SSH..." mkdir -p ~/.ssh chmod 700 ~/.ssh echo "Adding deploy key..." echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa echo "Testing SSH connection..." ssh -T -p 2222 git@git.np-dms.work || true echo "Adding known hosts..." ssh-keyscan -p 2222 git.np-dms.work >> ~/.ssh/known_hosts 2>/dev/null || echo "ssh-keyscan failed, trying manual known hosts" # Add actual host key echo "[git.np-dms.work]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC3ii1sfG2oUWfO6xW9RNXWJNT4Qnv/jE7b/2u8lqpyrbmFs5GWflj7AwGkYL1OIXb5oamOJVAnPyypDnxPmsqEwCoqBeytkgl4GG5ToDju7nXLFB83C8o8yIgpNjM8hWM8cbbSRo45eRV8uR42F8Z/bfCYAthJCAEXs0g3OBAHOk9nSCxGXbS46WTuV4FhfbnjlJ6a8rfSjYoJ3kRaw9kyCr5vzQfvE4As4nLy4iZWM1g/Nif1G2n+CHBH5wlOCfcjDBkPfjJ7Xmoo99ZiQUqFsYaQ78qZFbtKrcMkGfhPvo1tGAcEJeZJft+gbONHAJy1462nEjqDKcq94V5TYNK10O/Qy+JB1eLrGVeeyjKep+tCtJrIVzeXtniphl0G2TYjUmdSUFVGOO1Ku1v0c+1yIqxVoe8uI/d7PCae3U0wAHPWCoX7G5DILaeW8YpBVqhInugjuXsgIuRPt08xO2AXV3CLM5AKWMfsufz5hIR+gga7sCSofBpYRyR8w6Ov8NdfVVjFix8K1XuAz1R7RCTwtMvI3Ft+HYiICvKbVotx6w8mbI5klpOSc3O76KhcYf6sQS/wu8SDBATFhOzbfoIaS3kaZtDwThKLnjjwDwiE9ivoK2W8p7pCJkB1WVdNkiE4E12q8P+R90Sp984GRGlIdz0uf7EXZIxsSomHh5TarQ==" >> ~/.ssh/known_hosts echo "Cloning repository..." git clone --depth 1 ssh://git@git.np-dms.work:2222/np-dms/lcbp3.git . echo "Fetching and resetting to main..." git fetch origin main git reset --hard origin/main echo "Checkout completed successfully" # ── [1] pnpm ต้องมาก่อน setup-node เสมอ ──────────────── - name: ⚙️ Install pnpm uses: pnpm/action-setup@v4 # version อ่านจาก packageManager field ใน package.json อัตโนมัติ - name: ⚙️ Setup Node uses: actions/setup-node@v4 with: node-version: 20 # ลบ cache: "pnpm" ออก — ใช้ volume mount บน runner แทน # ── [2] ชี้ store ไปที่ volume ที่ mount ไว้ ───────────── - name: 🔧 Set pnpm store path run: pnpm config set store-dir /root/.local/share/pnpm - name: 📦 Install deps run: pnpm install --frozen-lockfile - name: 🧹 Lint run: pnpm lint - name: 🔍 Security & quality checks run: | # UUID misuse check (ADR-019) if grep -r --include="*.ts" --include="*.tsx" --exclude-dir={node_modules,.next,.agents,.git,scripts,test,__tests__} "parseInt(.*uuid" .; then echo "❌ UUID misuse detected" exit 1 fi # console.log check (Clean Code) if grep -r --include="*.ts" --include="*.tsx" --exclude-dir={node_modules,.next,.agents,.git,scripts,test,__tests__} "console.log" .; then echo "❌ console.log detected" exit 1 fi # ── [3] แยก step — เห็น failure ได้ชัดขึ้น ────────────── - name: 🧪 Test backend run: pnpm test working-directory: backend - name: 🧪 Test frontend run: pnpm test run working-directory: frontend # ============================================================ # JOB 2 : Deploy — Trigger Blue-Green on QNAP # ============================================================ deploy: needs: build if: github.ref == 'refs/heads/main' runs-on: self-hosted steps: - name: � Checkout uses: actions/checkout@v4 - name: 🔐 Setup SSH and Deploy to QNAP run: | # Ensure sshpass is available (install if needed) if ! command -v sshpass &> /dev/null; then apt-get update -qq && apt-get install -y -qq sshpass fi # Create remote deployment script REMOTE_SCRIPT=$(cat << 'SCRIPT_EOF' set -e export PATH="/share/CACHEDEV1_DATA/.qpkg/container-station/bin:/opt/bin:/usr/local/bin:/usr/bin:/bin:$PATH" echo "==========================================" echo "Starting QNAP Deployment Process" echo "==========================================" # Verify Docker is accessible if ! docker version > /dev/null 2>&1; then echo "✗ Docker not accessible. Check Container Station." exit 1 fi echo "✓ Docker accessible" # Sync scripts first echo "📂 Syncing deployment scripts..." cd /share/np-dms/app/source/lcbp3 # Check if directory exists if [ ! -d ".git" ]; then echo "✗ Git repository not found at expected path" exit 1 fi git fetch origin main git reset --hard origin/main echo "✓ Code synced" # Ensure scripts are executable chmod +x scripts/deploy.sh scripts/rollback.sh 2>/dev/null || true mkdir -p /share/np-dms/app/logs # Note: Docker build cache is preserved for faster builds # Only prune cache manually when needed: docker builder prune -f echo "🚀 Executing deployment..." ./scripts/deploy.sh echo "✓ Deployment completed successfully" SCRIPT_EOF ) # Retry logic for SSH connection max_attempts=3 attempt=1 while [ $attempt -le $max_attempts ]; do echo "🚀 Deployment attempt $attempt/$max_attempts..." if echo "$REMOTE_SCRIPT" | sshpass -p "${{ secrets.PASSWORD }}" ssh -o StrictHostKeyChecking=no \ -o ConnectTimeout=60 \ -o ServerAliveInterval=30 \ -o ServerAliveCountMax=60 \ -o TCPKeepAlive=yes \ -p ${{ secrets.PORT }} ${{ secrets.USERNAME }}@${{ secrets.HOST }} 'bash -s'; then echo "✅ Deployment successful!" exit 0 else echo "⚠️ Attempt $attempt failed" if [ $attempt -lt $max_attempts ]; then echo "⏳ Retrying in 10 seconds..." sleep 10 fi fi attempt=$((attempt + 1)) done echo "❌ All deployment attempts failed" exit 1 timeout-minutes: 20