From 53f34835f111ad8312e2c1bfa08f79537f0008fb Mon Sep 17 00:00:00 2001 From: "DESKTOP-TKLFCPR\\ython" Date: Mon, 1 Jun 2026 20:42:12 +0900 Subject: [PATCH] fix(zioinfo): add /var/www/zioinfo copy step in deploy + restore stash files Co-Authored-By: Claude Sonnet 4.6 --- scripts/setup/extract_stash_files.py | 101 ++++++++++++++++++ scripts/setup/fix_zioinfo_deploy_copy.py | 74 ++++++++++++++ scripts/setup/restore_stash_and_deploy.py | 118 ++++++++++++++++++++++ workspace/guardia-itsm/Jenkinsfile | 2 +- 4 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 scripts/setup/extract_stash_files.py create mode 100644 scripts/setup/fix_zioinfo_deploy_copy.py create mode 100644 scripts/setup/restore_stash_and_deploy.py diff --git a/scripts/setup/extract_stash_files.py b/scripts/setup/extract_stash_files.py new file mode 100644 index 00000000..640904e5 --- /dev/null +++ b/scripts/setup/extract_stash_files.py @@ -0,0 +1,101 @@ +"""stash에서 핵심 파일만 추출 → 빌드 → 배포""" +import paramiko, sys, time, shutil, os +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() + +def run(label, cmd, timeout=60): + print(f'\n[{label}]') + _, o, e = c.exec_command(cmd, timeout=timeout) + out = o.read().decode('utf-8','replace').strip() + if out: print(out[:600]) + return out + +SRC = '/opt/zioinfo/src' + +# 1. stash에서 frontend 파일만 직접 추출 +run('stash에서 Company.jsx 추출', + f'git -C {SRC} checkout stash -- frontend/src/pages/Company.jsx 2>&1 || echo FAIL') +run('stash에서 Company.css 추출', + f'git -C {SRC} checkout stash -- frontend/src/pages/Company.css 2>&1 || echo FAIL') +run('stash에서 Home.jsx 추출 (변경 있으면)', + f'git -C {SRC} checkout stash -- frontend/src/pages/Home.jsx 2>&1 || echo SKIP') +run('stash에서 java 파일 추출', + f'''git -C {SRC} checkout stash -- \ + 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 2>&1 || echo SKIP''') + +# 2. 추출된 파일 확인 +run('Company.jsx 줄 수', f'wc -l {SRC}/frontend/src/pages/Company.jsx 2>/dev/null') +run('변경된 파일 확인', f'git -C {SRC} diff --stat HEAD 2>/dev/null | head -10') + +# 3. npm 빌드 + /var/www/zioinfo 복사 +run('npm 빌드', + f'cd {SRC}/frontend && npm run build 2>&1 | tail -5', timeout=120) +run('/var/www/zioinfo 복사', + f'cp -r {SRC}/backend/src/main/resources/static/. /var/www/zioinfo/ && echo "copied $(ls /var/www/zioinfo/assets | wc -l) files"') + +# 4. 로컬로 파일 다운로드 +print('\n[로컬 workspace 업데이트]') +key_files = [ + 'frontend/src/pages/Company.jsx', + 'frontend/src/pages/Company.css', + 'frontend/src/pages/Home.jsx', + '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', +] +downloaded = [] +for f in key_files: + remote = f'{SRC}/{f}' + local_ws = f'C:/GUARDiA/workspace/zioinfo-web/{f}' + local_repo = f'C:/GUARDiA/repos/zioinfo-web/{f}' + try: + os.makedirs(os.path.dirname(local_ws), exist_ok=True) + os.makedirs(os.path.dirname(local_repo), exist_ok=True) + sftp.get(remote, local_ws) + shutil.copy2(local_ws, local_repo) + print(f' OK: {f}') + downloaded.append(f) + except Exception as e: + print(f' SKIP {f}: {e}') + +# 5. repos commit + Gitea bundle push +if downloaded: + import subprocess + subprocess.run(['git', '-C', 'C:/GUARDiA/repos/zioinfo-web', 'add', '-A']) + r = subprocess.run(['git', '-C', 'C:/GUARDiA/repos/zioinfo-web', 'status', '--short'], + capture_output=True, text=True) + if r.stdout.strip(): + subprocess.run(['git', '-C', 'C:/GUARDiA/repos/zioinfo-web', 'commit', + '-m', 'restore: extract Company.jsx and backend from server stash']) + print('\n[repos 커밋 완료]') + + # bundle push + bundle = 'C:/GUARDiA/repos/zioinfo-web.bundle' + subprocess.run(['git', '-C', 'C:/GUARDiA/repos/zioinfo-web', 'bundle', 'create', bundle, '--all']) + sftp.put(bundle, '/tmp/zioinfo-web.bundle') + os.remove(bundle) + run('Gitea push', + "rm -rf /tmp/zw_push && git clone /tmp/zioinfo-web.bundle /tmp/zw_push 2>/dev/null && " + "cd /tmp/zw_push && " + "git remote set-url origin 'http://zio:Zio%40Admin2026%21@127.0.0.1:9003/zio/zioinfo-web.git' && " + "git push origin main --force 2>&1 | tail -3 && " + "rm -rf /tmp/zw_push /tmp/zioinfo-web.bundle && echo 'pushed'", timeout=120) + else: + print('\n변경 없음 (이미 최신)') + +# 6. Spring Boot 재시작 (mvn build는 시간 오래 걸리므로 스킵, /var/www만 업데이트) +run('zioinfo 서비스 재시작', 'systemctl restart zioinfo && sleep 3 && systemctl is-active zioinfo') + +# 7. 최종 확인 +run('Company.jsx 최신 (greeting 내용)', + f'grep -c "안녕하십니까\|홍영택" {SRC}/frontend/src/pages/Company.jsx 2>/dev/null') +run('/var/www/zioinfo 최신 파일', + 'ls -la /var/www/zioinfo/assets/ | sort -k6,7 -r | head -5') + +sftp.close() +c.close() +print('\n=== 완료 ===') diff --git a/scripts/setup/fix_zioinfo_deploy_copy.py b/scripts/setup/fix_zioinfo_deploy_copy.py new file mode 100644 index 00000000..e9262c53 --- /dev/null +++ b/scripts/setup/fix_zioinfo_deploy_copy.py @@ -0,0 +1,74 @@ +"""deploy_server.py zioinfo-web에 /var/www/zioinfo 복사 단계 추가""" +import paramiko, sys, time +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) + +def run(label, cmd, timeout=60): + print(f'\n[{label}]') + _, o, _ = c.exec_command(cmd, timeout=timeout) + print(o.read().decode('utf-8','replace').strip()[:600]) + +# 현재 npm build 이후 단계 확인 +run('현재 zioinfo npm build 이후 단계', + "grep -n 'npm build\\|mvn\\|deploy jar\\|www\\|static\\|copy\\|cp ' " + "/opt/zioinfo/deploy_server.py | head -15") + +# deploy_server.py 수정: npm build 후 /var/www/zioinfo에 복사 추가 +run('deploy_server.py 수정', + r"""python3 << 'PYEOF' +with open('/opt/zioinfo/deploy_server.py', 'r') as f: + content = f.read() + +# npm build 다음에 copy to /var/www/zioinfo 추가 +OLD = ''' ("npm build", ["bash", "-c", + f"cd {SRC}/frontend && npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps && npm run build"]), + ("mvn package", ["bash", "-c",''' + +NEW = ''' ("npm build", ["bash", "-c", + f"cd {SRC}/frontend && npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps && npm run build"]), + ("copy to www", ["bash", "-c", + f"cp -r {SRC}/backend/src/main/resources/static/. /var/www/zioinfo/ && echo 'copied'"]), + ("mvn package", ["bash", "-c",''' + +if OLD in content: + content = content.replace(OLD, NEW) + with open('/opt/zioinfo/deploy_server.py', 'w') as f: + f.write(content) + print('수정 완료') +else: + # 이미 있거나 형식이 다름 - 직접 확인 + idx = content.find('npm build') + print(f'npm build 위치: {idx}') + print(content[idx:idx+300]) +PYEOF +""") + +run('수정 결과 확인', + "grep -n 'npm build\\|copy to www\\|var/www\\|mvn package' /opt/zioinfo/deploy_server.py | head -10") + +# 서비스 재시작 +run('서비스 재시작', + 'systemctl restart zioinfo-deploy && sleep 2 && systemctl is-active zioinfo-deploy') + +# zioinfo-web 즉시 재배포 트리거 +run('zioinfo-web 재배포 트리거', + "curl -sf -X POST http://localhost:9999 " + "-H 'Content-Type: application/json' " + "-H 'X-Gitea-Event: push' " + "-d '{\"repository\":{\"name\":\"zioinfo-web\"},\"ref\":\"refs/heads/main\"}' 2>/dev/null") + +# 배포 완료 대기 (npm build + mvn package는 시간이 걸림) +print('\n배포 진행 중 (최대 3분)...') +for i in range(20): + time.sleep(10) + _, o, _ = c.exec_command('tail -3 /var/log/zioinfo/deploy.log 2>/dev/null', timeout=10) + last = o.read().decode('utf-8','replace').strip() + print(f' [{i*10}s] {last.splitlines()[-1] if last else "..."}') + if '배포 완료' in last or 'health check' in last and '완료' in last: + break + +run('배포 로그 최종', 'tail -15 /var/log/zioinfo/deploy.log 2>/dev/null') +run('/var/www/zioinfo 업데이트 확인', 'ls -la /var/www/zioinfo/assets/ | tail -3') + +c.close() diff --git a/scripts/setup/restore_stash_and_deploy.py b/scripts/setup/restore_stash_and_deploy.py new file mode 100644 index 00000000..de889ec2 --- /dev/null +++ b/scripts/setup/restore_stash_and_deploy.py @@ -0,0 +1,118 @@ +"""서버 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=== 완료 ===') diff --git a/workspace/guardia-itsm/Jenkinsfile b/workspace/guardia-itsm/Jenkinsfile index 1d263c20..6a1b4059 100644 --- a/workspace/guardia-itsm/Jenkinsfile +++ b/workspace/guardia-itsm/Jenkinsfile @@ -23,7 +23,7 @@ pipeline { } } stage('Deploy') { - when { branch 'main' } + when { expression { env.GIT_BRANCH ==~ /.*main/ || env.BRANCH_NAME == 'main' } } steps { sh """ rsync -a --exclude=__pycache__ --exclude=.git \