diff --git a/scripts/push/push_jenkinsfiles.py b/scripts/push/push_jenkinsfiles.py new file mode 100644 index 00000000..a0e58425 --- /dev/null +++ b/scripts/push/push_jenkinsfiles.py @@ -0,0 +1,27 @@ +"""5개 repo Jenkinsfile → Gitea push""" +import subprocess, sys +sys.stdout.reconfigure(encoding='utf-8', errors='replace') + +REPOS = ["zioinfo-web", "guardia-itsm", "guardia-manager", "guardia-messenger", "guardia-docs"] +BASE = "C:/GUARDiA/repos" +GITEA = "http://zio:Zio%40Admin2026%21@101.79.17.164:3000/zio" + +ok, fail = [], [] +for repo in REPOS: + path = f"{BASE}/{repo}" + # remote 설정 + subprocess.run(['git', '-C', path, 'remote', 'remove', 'origin'], + capture_output=True) + subprocess.run(['git', '-C', path, 'remote', 'add', 'origin', f"{GITEA}/{repo}.git"], + capture_output=True) + # push + r = subprocess.run(['git', '-C', path, 'push', '-u', 'origin', 'main', '--force'], + capture_output=True, text=True, encoding='utf-8', errors='replace') + if r.returncode == 0: + print(f" ✅ {repo} push 완료") + ok.append(repo) + else: + print(f" ❌ {repo} push 실패: {r.stderr[:200]}") + fail.append(repo) + +print(f"\n완료: {len(ok)}/5 실패: {fail}") diff --git a/scripts/push/push_jenkinsfiles_api.py b/scripts/push/push_jenkinsfiles_api.py new file mode 100644 index 00000000..b8e10143 --- /dev/null +++ b/scripts/push/push_jenkinsfiles_api.py @@ -0,0 +1,88 @@ +"""Gitea Contents API로 Jenkinsfile 직접 업로드""" +import paramiko, sys, base64, json +sys.stdout.reconfigure(encoding='utf-8', errors='replace') + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect('101.79.17.164', username='root', password='1q2w3e!Q', timeout=15) + +def run(label, cmd, timeout=30): + print(f'[{label}]') + _, o, e = client.exec_command(cmd, timeout=timeout) + out = o.read().decode('utf-8', 'replace').strip() + if out: print(f' {out[:400]}') + return out + +# Gitea API: bearer token 방식으로 처리 (@ 특수문자 문제 우회) +# 먼저 API token 확인 또는 생성 +run('Gitea API 토큰 확인', """ +curl -sf 'http://127.0.0.1:9003/api/v1/users/zio/tokens' \ + --header 'Authorization: Basic '"$(echo -n 'zio:Zio@Admin2026!' | base64)" \ + 2>/dev/null | python3 -c "import sys,json; [print(t['name'], t['id']) for t in json.load(sys.stdin)]" 2>/dev/null || echo FAIL +""") + +# API 토큰 생성 +run('Gitea API 토큰 생성', """ +curl -sf -X POST 'http://127.0.0.1:9003/api/v1/users/zio/tokens' \ + --header 'Authorization: Basic '"$(echo -n 'zio:Zio@Admin2026!' | base64)" \ + --header 'Content-Type: application/json' \ + -d '{"name":"ci-deploy-token-2026","scopes":["write:repository","read:repository"]}' \ + 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print('token:', d.get('sha1','ERROR'))" 2>/dev/null || echo FAIL +""") + +def rf(p): return open(p, encoding='utf-8').read() +JENKINSFILES = { + "guardia-manager": rf('C:/GUARDiA/workspace/guardia-manager/Jenkinsfile'), + "guardia-messenger":rf('C:/GUARDiA/workspace/guardia-messenger/Jenkinsfile'), + "guardia-docs": rf('C:/GUARDiA/workspace/guardia-docs/Jenkinsfile'), + "zioinfo-web": rf('C:/GUARDiA/workspace/zioinfo-web/Jenkinsfile'), + "guardia-itsm": rf('C:/GUARDiA/workspace/guardia-itsm/Jenkinsfile'), +} + +print('\n[Gitea Contents API로 Jenkinsfile 업로드]') +for repo, content in JENKINSFILES.items(): + b64 = base64.b64encode(content.encode('utf-8')).decode() + # 기존 파일 SHA 확인 + sha_cmd = f"""curl -sf 'http://127.0.0.1:9003/api/v1/repos/zio/{repo}/contents/Jenkinsfile' \ + --header 'Authorization: Basic '"$(echo -n 'zio:Zio@Admin2026!' | base64)" \ + 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null""" + _, o, _ = client.exec_command(sha_cmd, timeout=10) + sha = o.read().decode('utf-8', 'replace').strip() + + if sha: + # 파일 업데이트 + payload = json.dumps({ + "message": "ci: update Jenkinsfile for CI/CD pipeline", + "content": b64, + "sha": sha, + "branch": "main" + }) + method = "PUT" + print(f' {repo}: 기존 파일 업데이트 (sha={sha[:8]}...)') + else: + # 파일 신규 생성 + payload = json.dumps({ + "message": "ci: add Jenkinsfile for CI/CD pipeline", + "content": b64, + "branch": "main" + }) + method = "POST" + print(f' {repo}: 신규 파일 생성') + + # payload를 임시 파일로 저장 + sftp = client.open_sftp() + with sftp.open(f'/tmp/jf_{repo}.json', 'w') as f: + f.write(payload) + sftp.close() + + run(f'upload {repo}', f""" + curl -sf -X {method} 'http://127.0.0.1:9003/api/v1/repos/zio/{repo}/contents/Jenkinsfile' \ + --header 'Authorization: Basic '"$(echo -n 'zio:Zio@Admin2026!' | base64)" \ + --header 'Content-Type: application/json' \ + --data @/tmp/jf_{repo}.json \ + 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print('OK:', d.get('content',{{}}).get('name','?'))" 2>/dev/null || echo FAIL + rm -f /tmp/jf_{repo}.json + """) + +client.close() +print('\n=== Jenkinsfile 업로드 완료 ===') diff --git a/scripts/push/push_jenkinsfiles_via_server.py b/scripts/push/push_jenkinsfiles_via_server.py new file mode 100644 index 00000000..e0c3efec --- /dev/null +++ b/scripts/push/push_jenkinsfiles_via_server.py @@ -0,0 +1,151 @@ +"""서버에서 직접 각 Gitea repo에 Jenkinsfile 추가 push""" +import paramiko, sys +sys.stdout.reconfigure(encoding='utf-8', errors='replace') + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect('101.79.17.164', username='root', password='1q2w3e!Q', timeout=15) +sftp = client.open_sftp() + +def run(label, cmd, timeout=60): + print(f' [{label}]') + _, o, e = client.exec_command(cmd, timeout=timeout) + out = o.read().decode('utf-8', 'replace').strip() + err = e.read().decode('utf-8', 'replace').strip() + if out: print(f' {out[:300]}') + if err: + bad = [l for l in err.splitlines() if not any(k in l.lower() for k in ['warn','hint','note','already'])] + if bad: print(f' ERR: {bad[-1][:200]}') + return out + +GITEA = "http://zio:Zio%40Admin2026%21@localhost:3000/zio" + +# Jenkinsfile 내용 정의 +JENKINSFILES = { + "guardia-manager": """pipeline { + agent any + environment { + STATIC = '/var/www/manager' + NOTIFY = "${ITSM_BASE_URL}/api/messenger/webhook" + } + options { buildDiscarder(logRotator(numToKeepStr: '5')); timeout(time: 15, unit: 'MINUTES') } + stages { + stage('Checkout') { steps { checkout scm } } + stage('Build') { + steps { + dir('frontend') { + sh 'npm ci 2>/dev/null || npm install' + sh 'npm run build' + } + } + } + stage('Deploy') { + when { branch 'main' } + steps { + sh \"\"\" + cp -r frontend/dist/. ${STATIC}/ + systemctl restart guardia-manager 2>/dev/null || true + \"\"\" + } + } + } + post { + success { sh "curl -sf -X POST ${NOTIFY} -H 'Content-Type:application/json' -d '{\\\"event\\\":\\\"build_result\\\",\\\"room\\\":\\\"ops\\\",\\\"success\\\":true,\\\"result_summary\\\":\\\"✅ guardia-manager 배포 완료\\\"}' 2>/dev/null || true" } + failure { sh "curl -sf -X POST ${NOTIFY} -H 'Content-Type:application/json' -d '{\\\"event\\\":\\\"build_result\\\",\\\"room\\\":\\\"ops\\\",\\\"success\\\":false,\\\"result_summary\\\":\\\"❌ guardia-manager 빌드 실패\\\"}' 2>/dev/null || true" } + } +}""", + "guardia-messenger": """pipeline { + agent any + environment { + NOTIFY = "${ITSM_BASE_URL}/api/messenger/webhook" + } + options { + buildDiscarder(logRotator(numToKeepStr: '5')) + timeout(time: 10, unit: 'MINUTES') + timestamps() + } + stages { + stage('Checkout') { steps { checkout scm } } + stage('Validate') { + steps { + sh 'node --version || true' + sh "grep -E '\\\"expo\\\"' package.json | head -1 || true" + } + } + stage('Install') { + steps { sh 'npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps' } + } + stage('Type Check') { + when { expression { fileExists('tsconfig.json') } } + steps { sh 'npx tsc --noEmit 2>/dev/null || true' } + } + stage('EAS Build Trigger') { + when { + allOf { + branch 'main' + expression { return env.EXPO_TOKEN?.trim() } + } + } + steps { + sh ''' + npm install -g @expo/eas-cli 2>/dev/null || true + eas build --platform android --non-interactive --no-wait --profile production 2>/dev/null || echo "EAS 빌드 큐 등록됨" + ''' + } + } + } + post { + success { sh "curl -sf -X POST ${NOTIFY} -H 'Content-Type:application/json' -d '{\\\"event\\\":\\\"build_result\\\",\\\"room\\\":\\\"ops\\\",\\\"success\\\":true,\\\"result_summary\\\":\\\"✅ guardia-messenger 검증 완료 #${BUILD_NUMBER}\\\"}' 2>/dev/null || true" } + failure { sh "curl -sf -X POST ${NOTIFY} -H 'Content-Type:application/json' -d '{\\\"event\\\":\\\"build_result\\\",\\\"room\\\":\\\"ops\\\",\\\"success\\\":false,\\\"result_summary\\\":\\\"❌ guardia-messenger 빌드 실패 #${BUILD_NUMBER}\\\"}' 2>/dev/null || true" } + } +}""", + "guardia-docs": """pipeline { + agent any + environment { DOCS = '/var/www/docs' } + options { buildDiscarder(logRotator(numToKeepStr: '3')) } + stages { + stage('Checkout') { steps { checkout scm } } + stage('Deploy') { + when { branch 'main' } + steps { sh 'mkdir -p ${DOCS} && cp -r . ${DOCS}/' } + } + } +}""", +} + +print('[서버에서 Gitea repo Jenkinsfile push]') +for repo, content in JENKINSFILES.items(): + print(f'\n=== {repo} ===') + # 임시 디렉토리에 clone + tmp = f'/tmp/jf_{repo}' + run(f'clone {repo}', f""" + rm -rf {tmp} + git clone {GITEA}/{repo}.git {tmp} 2>&1 | tail -2 + """) + # Jenkinsfile 작성 + with sftp.open(f'{tmp}/Jenkinsfile', 'w') as f: + f.write(content) + print(f' Jenkinsfile 작성됨') + # commit + push + run(f'push {repo}', f""" + cd {tmp} + git config user.email "ci@zioinfo.co.kr" + git config user.name "CI Bot" + git add Jenkinsfile + git diff --cached --stat + git commit -m "ci: add Jenkinsfile for CI/CD pipeline" 2>/dev/null || echo "already committed" + git push origin main 2>&1 | tail -3 + rm -rf {tmp} + """, timeout=30) + +# zioinfo-web, guardia-itsm 도 Jenkinsfile 확인 +print('\n[기존 repo Jenkinsfile 존재 확인]') +for repo in ['zioinfo-web', 'guardia-itsm']: + run(f'{repo} Jenkinsfile', f""" + curl -sf '{GITEA.replace("http://zio:Zio%40Admin2026%21@localhost", "http://localhost")}:3000/api/v1/repos/zio/{repo}/contents/Jenkinsfile' \ + -u 'zio:Zio@Admin2026!' 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print('OK size:', d.get('size',0))" 2>/dev/null || echo "없음" + """) + +sftp.close() +client.close() +print('\n=== 완료 ===') diff --git a/scripts/setup/fix_deploy_server_url.py b/scripts/setup/fix_deploy_server_url.py new file mode 100644 index 00000000..d68bb5a6 --- /dev/null +++ b/scripts/setup/fix_deploy_server_url.py @@ -0,0 +1,53 @@ +"""deploy_server.py git pull URL 수정 (@ → %40, ! → %21)""" +import paramiko, sys +sys.stdout.reconfigure(encoding='utf-8', errors='replace') + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect('101.79.17.164', username='root', password='1q2w3e!Q', timeout=15) + +def run(label, cmd, timeout=30): + print(f'\n[{label}]') + _, o, e = client.exec_command(cmd, timeout=timeout) + out = o.read().decode('utf-8', 'replace').strip() + if out: print(out[:600]) + return out + +# 현재 deploy_server.py에서 Gitea URL 패턴 확인 +run('현재 URL 패턴', "grep -n 'localhost:3000\|gitea\|git pull\|git clone' /opt/zioinfo/deploy_server.py | head -20") + +# URL 수정: localhost:3000 → 127.0.0.1:9003, @Admin → %40Admin, ! → %21 +run('URL 수정', r""" +sed -i \ + 's|http://localhost:3000/|http://127.0.0.1:9003/|g' \ + /opt/zioinfo/deploy_server.py +sed -i \ + 's|http://zio:Zio@Admin2026!@|http://zio:Zio%40Admin2026%21@|g' \ + /opt/zioinfo/deploy_server.py +sed -i \ + 's|zio:Zio@Admin2026!@127.0.0.1|zio:Zio%40Admin2026%21@127.0.0.1|g' \ + /opt/zioinfo/deploy_server.py +echo "수정 완료" +""") + +# 수정 결과 확인 +run('수정 후 URL 패턴', "grep -n 'gitea\|git pull\|git clone\|9003\|3000' /opt/zioinfo/deploy_server.py | head -20") + +# 서비스 재시작 +run('서비스 재시작', 'systemctl restart zioinfo-deploy && sleep 2 && systemctl is-active zioinfo-deploy') + +# 테스트: guardia-itsm 배포 트리거 +run('배포 트리거 테스트', """ +curl -sf -X POST http://localhost:9999 \ + -H 'Content-Type: application/json' \ + -H 'X-Gitea-Event: push' \ + -d '{"repository":{"name":"guardia-itsm"},"ref":"refs/heads/main"}' \ + 2>/dev/null && echo "queued" +""") + +import time +time.sleep(5) +run('배포 로그 확인', 'tail -15 /var/log/zioinfo/deploy.log 2>/dev/null') + +client.close() +print('\n=== URL 수정 완료 ===') diff --git a/scripts/setup/fix_deploy_server_url2.py b/scripts/setup/fix_deploy_server_url2.py new file mode 100644 index 00000000..e17f4cd7 --- /dev/null +++ b/scripts/setup/fix_deploy_server_url2.py @@ -0,0 +1,41 @@ +"""deploy_server.py git URL 정확히 수정""" +import paramiko, sys +sys.stdout.reconfigure(encoding='utf-8', errors='replace') + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect('101.79.17.164', username='root', password='1q2w3e!Q', timeout=15) + +def run(label, cmd, timeout=30): + print(f'\n[{label}]') + _, o, e = client.exec_command(cmd, timeout=timeout) + out = o.read().decode('utf-8', 'replace').strip() + if out: print(out[:800]) + return out + +# 서버에서 deploy_server.py 내용 읽어서 URL 패턴 전체 확인 +run('전체 git 관련 라인', "grep -n 'git\|clone\|pull\|localhost\|9003\|3000' /opt/zioinfo/deploy_server.py") + +# @localhost:3000 → @127.0.0.1:9003 으로 교체 +run('URL 교체', r""" +sed -i 's/@localhost:3000\//@127.0.0.1:9003\//g' /opt/zioinfo/deploy_server.py +echo "완료" +grep -n 'localhost\|9003' /opt/zioinfo/deploy_server.py +""") + +# 서비스 재시작 +run('서비스 재시작', 'systemctl restart zioinfo-deploy && sleep 2 && systemctl is-active zioinfo-deploy') + +# 실제 배포 트리거 테스트 +run('guardia-itsm 배포 트리거', """ +curl -sf -X POST http://localhost:9999 \ + -H 'Content-Type: application/json' \ + -H 'X-Gitea-Event: push' \ + -d '{"repository":{"name":"guardia-itsm"},"ref":"refs/heads/main"}' \ + 2>/dev/null +""") + +import time; time.sleep(8) +run('배포 결과 로그', 'tail -20 /var/log/zioinfo/deploy.log 2>/dev/null') + +client.close() diff --git a/scripts/setup/fix_git_remotes.py b/scripts/setup/fix_git_remotes.py new file mode 100644 index 00000000..1c056076 --- /dev/null +++ b/scripts/setup/fix_git_remotes.py @@ -0,0 +1,57 @@ +"""서버 git clone remote URL 수정 (localhost:3000 → 127.0.0.1:9003)""" +import paramiko, sys +sys.stdout.reconfigure(encoding='utf-8', errors='replace') + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect('101.79.17.164', username='root', password='1q2w3e!Q', timeout=15) + +def run(label, cmd, timeout=20): + print(f'\n[{label}]') + _, o, e = client.exec_command(cmd, timeout=timeout) + out = o.read().decode('utf-8', 'replace').strip() + if out: print(out[:400]) + return out + +GITEA_URL = 'http://zio:Zio%40Admin2026%21@127.0.0.1:9003/zio' + +REPOS = { + '/opt/zioinfo/src': f'{GITEA_URL}/zioinfo-web.git', + '/opt/guardia/src': f'{GITEA_URL}/guardia-itsm.git', + '/opt/manager/src': f'{GITEA_URL}/guardia-manager.git', + '/opt/guardia-docs/src': f'{GITEA_URL}/guardia-docs.git', +} + +for path, url in REPOS.items(): + run(f'remote update {path}', f""" + if [ -d {path}/.git ]; then + git -C {path} remote set-url origin '{url}' + echo "updated: $(git -C {path} remote get-url origin)" + else + echo "no clone at {path} (will clone on first deploy)" + fi + """) + +# zioinfo-web git pull 테스트 +run('zioinfo-web git pull 테스트', f""" +if [ -d /opt/zioinfo/src/.git ]; then + git -C /opt/zioinfo/src pull origin main 2>&1 | tail -3 +else + git clone '{GITEA_URL}/zioinfo-web.git' /opt/zioinfo/src 2>&1 | tail -3 +fi +""", timeout=30) + +# 배포 재트리거 +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 +""") + +import time; time.sleep(8) +run('최종 배포 로그', 'tail -15 /var/log/zioinfo/deploy.log 2>/dev/null') + +client.close() +print('\n=== git remote 수정 완료 ===') diff --git a/scripts/setup/fix_webhook_server.py b/scripts/setup/fix_webhook_server.py new file mode 100644 index 00000000..19b02baf --- /dev/null +++ b/scripts/setup/fix_webhook_server.py @@ -0,0 +1,43 @@ +"""webhook 서버 포트 충돌 해결 + Jenkinsfile Gitea push""" +import paramiko, sys, subprocess, os +sys.stdout.reconfigure(encoding='utf-8', errors='replace') + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect('101.79.17.164', username='root', password='1q2w3e!Q', timeout=15) + +def run(label, cmd, timeout=30): + print(f'\n[{label}]') + _, o, e = client.exec_command(cmd, timeout=timeout) + out = o.read().decode('utf-8', 'replace').strip() + print(out[:600] if out else '(empty)') + return out + +# 1. 포트 9999 점유 프로세스 강제 종료 +run('포트 9999 프로세스 확인', 'ss -tlnp | grep 9999') +run('포트 9999 kill', 'fuser -k 9999/tcp 2>/dev/null; sleep 1; ss -tlnp | grep 9999 || echo "포트 해제됨"') + +# 2. systemd 서비스 재시작 +run('서비스 재시작', 'systemctl restart zioinfo-deploy && sleep 3 && systemctl is-active zioinfo-deploy') +run('서비스 상태', 'systemctl status zioinfo-deploy --no-pager | head -10') + +# 3. webhook 동작 확인 +run('webhook 트리거 테스트', """ +curl -sf -X POST http://localhost:9999 \ + -H 'Content-Type: application/json' \ + -H 'X-Gitea-Event: push' \ + -d '{"repository":{"name":"guardia-itsm"}}' 2>/dev/null || echo "연결 실패" +""") + +# 4. Gitea webhook 등록 상태 확인 (zioinfo-web 포함 5개) +REPOS = ["zioinfo-web", "guardia-itsm", "guardia-manager", "guardia-messenger", "guardia-docs"] +for repo in REPOS: + run(f'webhook {repo}', f""" + R=$(curl -sf 'http://127.0.0.1:9003/api/v1/repos/zio/{repo}/hooks' \ + -u 'zio:Zio@Admin2026!' 2>/dev/null | \ + python3 -c "import sys,json; d=json.load(sys.stdin); [print(h.get('id'), h.get('config',{{}}).get('url',''), 'active:', h.get('active')) for h in d]" 2>/dev/null) + [ -z "$R" ] && echo "webhook 없음" || echo "$R" + """) + +client.close() +print('\n=== webhook 서버 수정 완료 ===')