215 lines
9.6 KiB
Python
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 구축 완료 ===')
|