zioinfo-mail/scripts/setup/setup_mail_cicd.py
2026-06-01 21:55:48 +09:00

215 lines
9.6 KiB
Python

"""zioinfo-mail CI/CD 전체 구축:
1. deploy_server.py에 zioinfo-mail 배포 함수 추가
2. Jenkins job 생성
3. Gitea webhook 등록 (port 9999 + Jenkins)
4. Jenkinsfile Gitea push
"""
import paramiko, sys, json, base64, subprocess, os, 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)
sftp = c.open_sftp()
G = base64.b64encode(b'zio:Zio@Admin2026!').decode()
J = 'http://127.0.0.1:9080'
A = 'admin:Admin@2026!'
TOK = 'gitea-build-2026'
def run(label, cmd, timeout=30):
print(f'\n[{label}]')
_, o, _ = c.exec_command(cmd, timeout=timeout)
out = o.read().decode('utf-8','replace').strip()
if out: print(out[:500])
return out
# crumb
_, o, _ = c.exec_command(f'curl -sf -u "{A}" {J}/crumbIssuer/api/json 2>/dev/null', timeout=10)
try:
cd = json.loads(o.read().decode('utf-8','replace').strip())
CH = f'{cd["crumbRequestField"]}: {cd["crumb"]}'
except:
CH = 'Jenkins-Crumb: x'
# ── 1. deploy_server.py 업데이트 ─────────────────────────────
print('\n━━ 1. deploy_server.py zioinfo-mail 추가 ━━')
update_script = r"""
import re
with open('/opt/zioinfo/deploy_server.py', 'r') as f:
content = f.read()
if 'zioinfo-mail' in content:
print('이미 있음')
else:
# guardia-docs 블록 이후에 zioinfo-mail 추가
# 또는 elif repo == "guardia-docs": 다음에 추가
MAIL_BLOCK = '''
elif repo == "zioinfo-mail":
SRC = "/opt/mail"
ok = run_steps(repo, [
("git pull", ["bash", "-c",
f"[ -d {SRC}/src/.git ] && git -C {SRC}/src fetch origin main && git -C {SRC}/src reset --hard origin/main"
f" || git clone 'http://zio:Zio%40Admin2026%21@127.0.0.1:9003/zio/zioinfo-mail.git' {SRC}/src"]),
("npm build", ["bash", "-c",
f"cd {SRC}/src/frontend && npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps && npm run build"]),
("copy dist", ["bash", "-c", f"cp -r {SRC}/src/dist/. /var/www/mail/"]),
("pip install", ["bash", "-c",
f"{SRC}/venv/bin/pip install -r {SRC}/src/backend/requirements.txt -q"]),
("rsync", ["bash", "-c",
f"rsync -a --exclude=__pycache__ --exclude=.git --exclude='*.pyc' --exclude='.env' {SRC}/src/backend/ {SRC}/backend/"]),
("restart", ["systemctl", "restart", "zioinfo-mail"]),
("health check", ["bash", "-c", "sleep 4 && curl -sf http://localhost:8026/health"]),
])
if ok:
notify_itsm(True, "\\u2705 zioinfo-mail \\ubc30\\ud3ec \\uc644\\ub8cc")
else:
notify_itsm(False, "\\u274c zioinfo-mail \\ube4c\\ub4dc \\uc2e4\\ud328")
'''
# guardia-docs 블록 직전에 삽입
content = content.replace(
' elif repo == "guardia-docs":',
MAIL_BLOCK + ' elif repo == "guardia-docs":'
)
with open('/opt/zioinfo/deploy_server.py', 'w') as f:
f.write(content)
print('추가 완료')
"""
with sftp.open('/tmp/upd.py', 'w') as f: f.write(update_script)
run('deploy_server 업데이트', 'python3 /tmp/upd.py 2>&1; rm /tmp/upd.py')
run('zioinfo-mail 추가 확인', "grep -n 'zioinfo-mail' /opt/zioinfo/deploy_server.py")
run('webhook 서버 재시작', 'systemctl restart zioinfo-deploy && sleep 2 && systemctl is-active zioinfo-deploy')
# ── 2. Jenkins job 생성 ──────────────────────────────────────
print('\n━━ 2. Jenkins job 생성 (zioinfo-mail) ━━')
job_config = f"""<?xml version='1.1' encoding='UTF-8'?>
<flow-definition plugin="workflow-job">
<description>GUARDiA zioinfo-mail Webmail CI/CD</description>
<keepDependencies>false</keepDependencies>
<properties>
<org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty/>
<authToken>{TOK}</authToken>
</properties>
<definition class="org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition" plugin="workflow-cps">
<scm class="hudson.plugins.git.GitSCM" plugin="git">
<configVersion>2</configVersion>
<userRemoteConfigs>
<hudson.plugins.git.UserRemoteConfig>
<url>http://127.0.0.1:9003/zio/zioinfo-mail.git</url>
<credentialsId>gitea-zio</credentialsId>
</hudson.plugins.git.UserRemoteConfig>
</userRemoteConfigs>
<branches>
<hudson.plugins.git.BranchSpec><name>*/main</name></hudson.plugins.git.BranchSpec>
</branches>
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
<submoduleCfg class="empty-list"/>
<extensions/>
</scm>
<scriptPath>Jenkinsfile</scriptPath>
<lightweight>true</lightweight>
</definition>
<triggers>
<com.cloudbees.jenkins.gitea.GiteaWebHookTrigger plugin="gitea">
<properties/>
</com.cloudbees.jenkins.gitea.GiteaWebHookTrigger>
</triggers>
</flow-definition>"""
with sftp.open('/tmp/job_mail.xml', 'w') as f: f.write(job_config)
run('Jenkins job 생성',
f'curl -sf -X POST "{J}/createItem?name=zioinfo-mail" '
f'-u "{A}" -H "{CH}" '
f'-H "Content-Type: text/xml" '
f'--data-binary @/tmp/job_mail.xml 2>/dev/null; echo $?')
# authToken 설정
run('authToken config.xml 설정',
f'curl -sf -u "{A}" {J}/job/zioinfo-mail/config.xml 2>/dev/null | '
f'grep -c "authToken" || echo "없음"')
# ── 3. Gitea webhook 등록 ────────────────────────────────────
print('\n━━ 3. Gitea webhook 등록 ━━')
# 3-1. port 9999 webhook
payload9999 = json.dumps({
"type": "gitea",
"config": {"url": "http://127.0.0.1:9999", "content_type": "json",
"secret": "zioinfo-deploy-2026"},
"events": ["push"], "active": True
})
with sftp.open('/tmp/h1.json', 'w') as f: f.write(payload9999)
run('webhook 9999 등록',
f'curl -sf -X POST "http://127.0.0.1:9003/api/v1/repos/zio/zioinfo-mail/hooks" '
f'-H "Authorization: Basic {G}" -H "Content-Type: application/json" '
f'--data @/tmp/h1.json 2>/dev/null | '
'python3 -c "import sys,json; d=json.load(sys.stdin); print(\'id:\',d.get(\'id\'),d.get(\'config\',{}).get(\'url\',\'\'))" 2>/dev/null')
# 3-2. Jenkins webhook
payload_jenkins = json.dumps({
"type": "gitea",
"config": {"url": f"http://127.0.0.1:9080/job/zioinfo-mail/build?token={TOK}",
"content_type": "json"},
"events": ["push"], "active": True
})
with sftp.open('/tmp/h2.json', 'w') as f: f.write(payload_jenkins)
run('webhook Jenkins 등록',
f'curl -sf -X POST "http://127.0.0.1:9003/api/v1/repos/zio/zioinfo-mail/hooks" '
f'-H "Authorization: Basic {G}" -H "Content-Type: application/json" '
f'--data @/tmp/h2.json 2>/dev/null | '
'python3 -c "import sys,json; d=json.load(sys.stdin); print(\'id:\',d.get(\'id\'),d.get(\'config\',{}).get(\'url\',\'\'))" 2>/dev/null')
# ── 4. Jenkinsfile Gitea push ─────────────────────────────────
print('\n━━ 4. Jenkinsfile → Gitea push ━━')
jf_content = open('C:/GUARDiA/workspace/zioinfo-mail/Jenkinsfile', encoding='utf-8').read()
jf_b64 = base64.b64encode(jf_content.encode('utf-8')).decode()
# 기존 파일 SHA 확인
_, o, _ = c.exec_command(
f'curl -sf "http://127.0.0.1:9003/api/v1/repos/zio/zioinfo-mail/contents/Jenkinsfile" '
f'-H "Authorization: Basic {G}" 2>/dev/null | '
'python3 -c "import sys,json; print(json.load(sys.stdin).get(\'sha\',\'\'))" 2>/dev/null', timeout=10)
sha = o.read().decode('utf-8','replace').strip()
if sha:
payload_jf = json.dumps({"message":"ci: add Jenkinsfile","content":jf_b64,"sha":sha,"branch":"main"})
method = "PUT"
else:
payload_jf = json.dumps({"message":"ci: add Jenkinsfile","content":jf_b64,"branch":"main"})
method = "POST"
with sftp.open('/tmp/jf.json', 'w') as f: f.write(payload_jf)
run('Jenkinsfile Gitea 업로드',
f'curl -sf -X {method} "http://127.0.0.1:9003/api/v1/repos/zio/zioinfo-mail/contents/Jenkinsfile" '
f'-H "Authorization: Basic {G}" -H "Content-Type: application/json" '
f'--data @/tmp/jf.json 2>/dev/null | '
'python3 -c "import sys,json; d=json.load(sys.stdin); print(\'OK:\', d.get(\'content\',{}).get(\'name\',\'?\'))" 2>/dev/null')
# ── 5. 검증 ─────────────────────────────────────────────────
print('\n━━ 5. 최종 검증 ━━')
time.sleep(3)
run('Jenkins jobs 전체',
f'curl -sf -u "{A}" {J}/api/json 2>/dev/null | '
'python3 -c "import sys,json; [print(j[\'name\'].ljust(22),j[\'color\']) for j in json.load(sys.stdin)[\'jobs\']]" 2>/dev/null')
run('zioinfo-mail hooks 확인',
f'curl -sf "http://127.0.0.1:9003/api/v1/repos/zio/zioinfo-mail/hooks" '
f'-H "Authorization: Basic {G}" 2>/dev/null | '
'python3 -c "import sys,json; [print(\' hook\',h[\'id\'],h[\'config\'].get(\'url\',\'\')[:60],\'active:\',h[\'active\']) for h in json.load(sys.stdin)]" 2>/dev/null')
# 첫 빌드 트리거
run('첫 빌드 트리거',
f'curl -sf -X POST -u "{A}" -H "{CH}" {J}/job/zioinfo-mail/build 2>/dev/null && echo "트리거됨"')
time.sleep(10)
run('빌드 상태',
f'curl -sf -u "{A}" {J}/job/zioinfo-mail/api/json 2>/dev/null | '
'python3 -c "import sys,json; d=json.load(sys.stdin); '
'lb=d.get(\'lastBuild\',{}); print(\'build #\'+str(lb.get(\'number\',\'?\')))" 2>/dev/null')
sftp.close(); c.close()
print('\n=== zioinfo-mail CI/CD 구축 완료 ===')