"""서버 stash 복원 → 파일 추출 → 로컬 workspace 업데이트 → Gitea push → 배포""" import paramiko, sys, time, json, base64 sys.stdout.reconfigure(encoding='utf-8', errors='replace') c = paramiko.SSHClient(); c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect('101.79.17.164', username='root', password='1q2w3e!Q', timeout=15) sftp = c.open_sftp() GITEA_B64 = base64.b64encode(b'zio:Zio@Admin2026!').decode() def run(label, cmd, timeout=30): print(f'\n[{label}]') _, o, e = c.exec_command(cmd, timeout=timeout) out = o.read().decode('utf-8','replace').strip() err = e.read().decode('utf-8','replace').strip() if out: print(out[:600]) if err and 'warning' not in err.lower() and 'hint' not in err.lower(): bad = [l for l in err.splitlines() if not any(k in l.lower() for k in ['warn','hint','note'])] if bad: print(f' ERR: {bad[-1][:200]}') return out # 1. stash pop (충돌 시 ours 전략으로) run('stash pop 시도', 'cd /opt/zioinfo/src && ' 'git stash pop 2>&1 | head -20') # 2. 충돌 파일 확인 및 처리 run('충돌 파일 확인', 'cd /opt/zioinfo/src && git status --short 2>/dev/null | head -20') # 충돌 시 stash의 버전을 우선 적용 run('충돌 해결 (stash 버전 우선)', """cd /opt/zioinfo/src if git status --short 2>/dev/null | grep -q '^UU\|^AA\|^DD'; then git checkout --theirs . 2>/dev/null git add . 2>/dev/null echo "충돌 해결: stash 버전 적용" else echo "충돌 없음" fi git status --short | head -10 """) # 3. 변경된 핵심 파일 확인 run('변경된 파일 (frontend)', 'cd /opt/zioinfo/src && git diff HEAD --name-only -- frontend/ 2>/dev/null | head -20') # 4. 핵심 파일들을 로컬로 가져오기 print('\n[핵심 파일 다운로드]') key_files = [ 'frontend/src/pages/Company.jsx', 'frontend/src/pages/Company.css', 'backend/src/main/java/kr/co/zioinfo/web/config/DataInitializer.java', 'backend/src/main/java/kr/co/zioinfo/web/controller/AdminController.java', 'backend/src/main/java/kr/co/zioinfo/web/controller/ApiController.java', ] import os updated = [] for f in key_files: remote_path = f'/opt/zioinfo/src/{f}' local_path = f'C:/GUARDiA/workspace/zioinfo-web/{f}' local_dir = os.path.dirname(local_path) os.makedirs(local_dir, exist_ok=True) try: sftp.get(remote_path, local_path) print(f' 다운로드: {f}') updated.append(f) except Exception as e: print(f' SKIP {f}: {e}') # 5. repos/zioinfo-web에도 복사 print('\n[repos/zioinfo-web 업데이트]') for f in updated: src = f'C:/GUARDiA/workspace/zioinfo-web/{f}' dst = f'C:/GUARDiA/repos/zioinfo-web/{f}' dst_dir = os.path.dirname(dst) os.makedirs(dst_dir, exist_ok=True) import shutil shutil.copy2(src, dst) print(f' 복사: {f}') # 6. repos/zioinfo-web commit import subprocess result = subprocess.run( ['git', '-C', 'C:/GUARDiA/repos/zioinfo-web', 'status', '--short'], capture_output=True, text=True) if result.stdout.strip(): subprocess.run(['git', '-C', 'C:/GUARDiA/repos/zioinfo-web', 'add', '-A']) subprocess.run(['git', '-C', 'C:/GUARDiA/repos/zioinfo-web', 'commit', '-m', 'restore: recover yesterday work from server stash']) print('\n[repos 커밋 완료]') else: print('\n변경 없음 - stash가 이미 반영된 상태') # 7. 서버에서 직접 빌드 + /var/www/zioinfo 업데이트 run('서버 직접 빌드 + 배포', """cd /opt/zioinfo/src # npm build echo "=npm build=" cd frontend && npm run build 2>&1 | tail -3 cd .. # copy to www echo "=copy to www=" cp -r backend/src/main/resources/static/. /var/www/zioinfo/ echo "=copy done=" ls /var/www/zioinfo/assets/ | wc -l """, timeout=120) # 8. 서비스 재시작 run('서비스 재시작', 'systemctl restart zioinfo && sleep 5 && systemctl is-active zioinfo') # 9. 확인 run('/var/www/zioinfo 업데이트 확인', 'ls -la /var/www/zioinfo/assets/ | sort -k6,7 | tail -5') sftp.close() c.close() print('\n=== 완료 ===')