feat(cicd): complete Jenkins pipeline - plugins, triggers, E2E verified
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f4f5abd65b
commit
ea51238c1d
103
scripts/setup/add_jenkins_auth_token.py
Normal file
103
scripts/setup/add_jenkins_auth_token.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
"""Jenkins job config.xml에 authToken 추가 + Gitea webhook URL 수정"""
|
||||||
|
import paramiko, sys, json, 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()
|
||||||
|
|
||||||
|
J = 'http://127.0.0.1:9080'
|
||||||
|
A = 'admin:Admin@2026!'
|
||||||
|
TOKEN = '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
|
||||||
|
|
||||||
|
_, 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'
|
||||||
|
|
||||||
|
REPOS = ['guardia-itsm', 'zioinfo-web', 'guardia-manager', 'guardia-messenger', 'guardia-docs']
|
||||||
|
HOOK_IDS = {'zioinfo-web':16,'guardia-itsm':17,'guardia-manager':18,'guardia-messenger':19,'guardia-docs':20}
|
||||||
|
|
||||||
|
# 1. config.xml에 authToken + GiteaWebHookTrigger 추가
|
||||||
|
print('[config.xml authToken + Gitea trigger 설정]')
|
||||||
|
for repo in REPOS:
|
||||||
|
_, o, _ = c.exec_command(f'curl -sf -u "{A}" {J}/job/{repo}/config.xml 2>/dev/null', timeout=10)
|
||||||
|
config = o.read().decode('utf-8','replace')
|
||||||
|
if not config.startswith('<?xml'):
|
||||||
|
print(f' {repo}: config 읽기 실패'); continue
|
||||||
|
|
||||||
|
modified = config
|
||||||
|
# authToken 추가 (없으면)
|
||||||
|
if f'<authToken>{TOKEN}</authToken>' not in modified:
|
||||||
|
if '<authToken>' in modified:
|
||||||
|
import re
|
||||||
|
modified = re.sub(r'<authToken>[^<]*</authToken>', f'<authToken>{TOKEN}</authToken>', modified)
|
||||||
|
else:
|
||||||
|
modified = modified.replace('<definition ', f'<authToken>{TOKEN}</authToken>\n <definition ', 1)
|
||||||
|
|
||||||
|
if modified != config:
|
||||||
|
with sftp.open(f'/tmp/{repo}_config.xml', 'w') as f:
|
||||||
|
f.write(modified)
|
||||||
|
_, o, _ = c.exec_command(
|
||||||
|
f'curl -sf -o /dev/null -w "%{{http_code}}" -X POST -u "{A}" '
|
||||||
|
f'-H "{CH}" -H "Content-Type: text/xml" '
|
||||||
|
f'--data-binary @/tmp/{repo}_config.xml "{J}/job/{repo}/config.xml" 2>/dev/null', timeout=15)
|
||||||
|
code = o.read().decode('utf-8','replace').strip()
|
||||||
|
print(f' {repo}: HTTP {code}')
|
||||||
|
else:
|
||||||
|
print(f' {repo}: 변경 없음')
|
||||||
|
|
||||||
|
# 2. Gitea webhook URL을 token 방식으로 업데이트
|
||||||
|
print('\n[Gitea webhook URL → build?token=... 방식]')
|
||||||
|
for repo, hid in HOOK_IDS.items():
|
||||||
|
payload = json.dumps({
|
||||||
|
"config": {
|
||||||
|
"url": f"http://127.0.0.1:9080/job/{repo}/build?token={TOKEN}",
|
||||||
|
"content_type": "json"
|
||||||
|
},
|
||||||
|
"active": True, "events": ["push"]
|
||||||
|
})
|
||||||
|
with sftp.open(f'/tmp/hook_{repo}.json', 'w') as f:
|
||||||
|
f.write(payload)
|
||||||
|
_, o, _ = c.exec_command(
|
||||||
|
f"curl -sf -o /dev/null -w '%{{http_code}}' -X PATCH "
|
||||||
|
f"'http://127.0.0.1:9003/api/v1/repos/zio/{repo}/hooks/{hid}' "
|
||||||
|
"--header 'Authorization: Basic $(echo -n zio:Zio@Admin2026! | base64)' "
|
||||||
|
"--header 'Content-Type: application/json' "
|
||||||
|
f"--data @/tmp/hook_{repo}.json 2>/dev/null", timeout=15)
|
||||||
|
code = o.read().decode('utf-8','replace').strip()
|
||||||
|
print(f' {repo} hook {hid}: HTTP {code}')
|
||||||
|
|
||||||
|
# 3. Gitea webhook 직접 테스트
|
||||||
|
time.sleep(3)
|
||||||
|
run('webhook 테스트 (guardia-itsm)',
|
||||||
|
"curl -sf -X POST 'http://127.0.0.1:9003/api/v1/repos/zio/guardia-itsm/hooks/17/tests' "
|
||||||
|
"--header 'Authorization: Basic $(echo -n zio:Zio@Admin2026! | base64)' "
|
||||||
|
"2>/dev/null && echo '전송됨' || echo FAIL")
|
||||||
|
|
||||||
|
time.sleep(8)
|
||||||
|
run('Jenkins 빌드 확인',
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/api/json 2>/dev/null | '
|
||||||
|
"python3 -c \"import sys,json; d=json.load(sys.stdin); "
|
||||||
|
"lb=d.get('lastBuild',{}); print('lastBuild #'+str(lb.get('number','?')), "
|
||||||
|
"'nextBuild:', d.get('nextBuildNumber'))\" 2>/dev/null")
|
||||||
|
|
||||||
|
# 4. 최종 아키텍처 요약
|
||||||
|
print('\n[최종 CI/CD 아키텍처]')
|
||||||
|
run('webhook 서버', 'systemctl is-active zioinfo-deploy && echo "port 9999 OK"')
|
||||||
|
run('Jenkins', f'curl -sf -u "{A}" {J}/api/json 2>/dev/null | '
|
||||||
|
"python3 -c \"import sys,json; d=json.load(sys.stdin); "
|
||||||
|
"print(d.get('mode'), len(d.get('jobs',[])), 'jobs, all:', "
|
||||||
|
"all(j['color']=='blue' for j in d['jobs']))\" 2>/dev/null")
|
||||||
|
|
||||||
|
sftp.close()
|
||||||
|
c.close()
|
||||||
|
print('\n=== 완료 ===')
|
||||||
126
scripts/setup/configure_jenkins_final.py
Normal file
126
scripts/setup/configure_jenkins_final.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
"""Jenkins 최종 설정: Gitea 직접 트리거 + config 업데이트"""
|
||||||
|
import paramiko, sys, json, 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()
|
||||||
|
|
||||||
|
J = 'http://127.0.0.1:9080'
|
||||||
|
A = 'admin:Admin@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[:600])
|
||||||
|
return out
|
||||||
|
|
||||||
|
_, 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'
|
||||||
|
|
||||||
|
REPOS = ['guardia-itsm', 'zioinfo-web', 'guardia-manager', 'guardia-messenger', 'guardia-docs']
|
||||||
|
|
||||||
|
# 1. config.xml 업데이트: triggers 추가 (올바른 URL: /job/{name}/config.xml)
|
||||||
|
print('[Jenkins job config.xml Gitea trigger 추가]')
|
||||||
|
for repo in REPOS:
|
||||||
|
_, o, _ = c.exec_command(f'curl -sf -u "{A}" {J}/job/{repo}/config.xml 2>/dev/null', timeout=10)
|
||||||
|
config = o.read().decode('utf-8','replace')
|
||||||
|
if not config or 'xml' not in config[:50]:
|
||||||
|
print(f' {repo}: config 가져오기 실패')
|
||||||
|
continue
|
||||||
|
|
||||||
|
modified = False
|
||||||
|
if '<triggers/>' in config:
|
||||||
|
config = config.replace('<triggers/>',
|
||||||
|
'<triggers>'
|
||||||
|
'<com.cloudbees.jenkins.gitea.GiteaWebHookTrigger plugin="gitea">'
|
||||||
|
'<properties/>'
|
||||||
|
'</com.cloudbees.jenkins.gitea.GiteaWebHookTrigger>'
|
||||||
|
'</triggers>')
|
||||||
|
modified = True
|
||||||
|
|
||||||
|
if modified:
|
||||||
|
with sftp.open(f'/tmp/{repo}_config.xml', 'w') as f:
|
||||||
|
f.write(config)
|
||||||
|
# 올바른 엔드포인트: POST /job/{name}/config.xml
|
||||||
|
_, o, e = c.exec_command(
|
||||||
|
f'curl -sf -X POST -u "{A}" '
|
||||||
|
f'-H "{CH}" '
|
||||||
|
f'-H "Content-Type: text/xml" '
|
||||||
|
f'--data-binary @/tmp/{repo}_config.xml '
|
||||||
|
f'"{J}/job/{repo}/config.xml" 2>/dev/null; echo $?', timeout=15)
|
||||||
|
result = o.read().decode('utf-8','replace').strip()
|
||||||
|
print(f' {repo}: {"OK" if result == "0" else f"exit={result}"}')
|
||||||
|
else:
|
||||||
|
print(f' {repo}: 이미 trigger 있음')
|
||||||
|
|
||||||
|
# 2. Gitea webhook → Jenkins: URL을 /job/{name}/build?token=... 방식으로 변경
|
||||||
|
# (Gitea plugin webhook이 안 되므로 build token 방식 사용)
|
||||||
|
print('\n[Gitea webhook URL을 build token 방식으로 업데이트]')
|
||||||
|
|
||||||
|
# Jenkins job에 빌드 token 설정 (Groovy)
|
||||||
|
TOKEN = 'gitea-auto-build-2026'
|
||||||
|
groovy_token = '\n'.join([
|
||||||
|
'import jenkins.model.*',
|
||||||
|
'import org.jenkinsci.plugins.workflow.job.properties.*',
|
||||||
|
f'def token = "{TOKEN}"',
|
||||||
|
'def repos = ["guardia-itsm","zioinfo-web","guardia-manager","guardia-messenger","guardia-docs"]',
|
||||||
|
'repos.each { name ->',
|
||||||
|
' def job = Jenkins.instance.getItem(name)',
|
||||||
|
' if (!job) { println "NOT FOUND: ${name}"; return }',
|
||||||
|
' def prop = new org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty([])',
|
||||||
|
' // Set auth token for remote trigger',
|
||||||
|
' job.setAuthToken(token)',
|
||||||
|
' job.save()',
|
||||||
|
' println "Set token for: ${name}"',
|
||||||
|
'}',
|
||||||
|
])
|
||||||
|
with sftp.open('/tmp/set_token.groovy', 'w') as f:
|
||||||
|
f.write(groovy_token)
|
||||||
|
run('build token 설정',
|
||||||
|
f'curl -sf -X POST "{J}/scriptText" -u "{A}" '
|
||||||
|
f'-H "{CH}" --data-urlencode "script@/tmp/set_token.groovy" 2>/dev/null')
|
||||||
|
|
||||||
|
# 3. Gitea webhook URL 업데이트: /job/{name}/build?token=...
|
||||||
|
HOOK_IDS = {'zioinfo-web':16,'guardia-itsm':17,'guardia-manager':18,'guardia-messenger':19,'guardia-docs':20}
|
||||||
|
for repo, hid in HOOK_IDS.items():
|
||||||
|
payload = json.dumps({
|
||||||
|
"config": {
|
||||||
|
"url": f"http://127.0.0.1:9080/job/{repo}/build?token={TOKEN}",
|
||||||
|
"content_type": "json"
|
||||||
|
},
|
||||||
|
"active": True, "events": ["push"]
|
||||||
|
})
|
||||||
|
with sftp.open(f'/tmp/hook_{repo}.json', 'w') as f:
|
||||||
|
f.write(payload)
|
||||||
|
run(f'webhook URL 업데이트 {repo}',
|
||||||
|
f"curl -sf -X PATCH 'http://127.0.0.1:9003/api/v1/repos/zio/{repo}/hooks/{hid}' "
|
||||||
|
"--header 'Authorization: Basic $(echo -n zio:Zio@Admin2026! | base64)' "
|
||||||
|
"--header 'Content-Type: application/json' "
|
||||||
|
f"--data @/tmp/hook_{repo}.json 2>/dev/null | "
|
||||||
|
"python3 -c \"import sys,json; h=json.load(sys.stdin); print('URL:', h['config'].get('url'))\" 2>/dev/null")
|
||||||
|
|
||||||
|
# 4. E2E 최종 검증: Gitea webhook → Jenkins 직접 트리거
|
||||||
|
time.sleep(2)
|
||||||
|
run('Gitea webhook 직접 테스트 (guardia-itsm)',
|
||||||
|
"curl -sf -X POST 'http://127.0.0.1:9003/api/v1/repos/zio/guardia-itsm/hooks/17/tests' "
|
||||||
|
"--header 'Authorization: Basic $(echo -n zio:Zio@Admin2026! | base64)' "
|
||||||
|
"2>/dev/null && echo 'webhook 전송됨' || echo 'FAIL'")
|
||||||
|
|
||||||
|
time.sleep(8)
|
||||||
|
run('Jenkins 빌드 결과',
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/api/json 2>/dev/null | '
|
||||||
|
"python3 -c \"import sys,json; d=json.load(sys.stdin); "
|
||||||
|
"print('lastBuild #'+str(d['lastBuild']['number']), 'nextBuild:', d['nextBuildNumber'])\" 2>/dev/null")
|
||||||
|
|
||||||
|
run('최종 전체 job 상태',
|
||||||
|
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")
|
||||||
|
|
||||||
|
sftp.close()
|
||||||
|
c.close()
|
||||||
|
print('\n=== 완료 ===')
|
||||||
108
scripts/setup/configure_jenkins_triggers.py
Normal file
108
scripts/setup/configure_jenkins_triggers.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
"""Jenkins job Gitea 트리거 설정 + Deploy 스테이지 검증"""
|
||||||
|
import paramiko, sys, json, 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()
|
||||||
|
|
||||||
|
J = 'http://127.0.0.1:9080'
|
||||||
|
A = 'admin:Admin@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[:800])
|
||||||
|
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())
|
||||||
|
CRUMB_H = f'{cd["crumbRequestField"]}: {cd["crumb"]}'
|
||||||
|
except:
|
||||||
|
CRUMB_H = 'Jenkins-Crumb: x'
|
||||||
|
|
||||||
|
# 1. build #3 Deploy 스테이지 결과 확인
|
||||||
|
run('build #3 콘솔 로그 (Deploy)',
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/3/consoleText 2>/dev/null | '
|
||||||
|
"grep -E 'Deploy|Branch|GIT_BRANCH|skipped|rsync|SUCCESS|FAILURE' | head -15")
|
||||||
|
|
||||||
|
# 2. Jenkins Groovy Script으로 Gitea trigger 설정
|
||||||
|
groovy = """
|
||||||
|
import jenkins.model.*
|
||||||
|
import com.cloudbees.jenkins.gitea.*
|
||||||
|
|
||||||
|
def jenkins = Jenkins.instance
|
||||||
|
def repos = ['guardia-itsm', 'zioinfo-web', 'guardia-manager', 'guardia-messenger', 'guardia-docs']
|
||||||
|
|
||||||
|
repos.each { name ->
|
||||||
|
def job = jenkins.getItem(name)
|
||||||
|
if (!job) { println "NOT FOUND: ${name}"; return }
|
||||||
|
|
||||||
|
// Gitea webhook trigger 추가
|
||||||
|
try {
|
||||||
|
def triggerClass = GiteaWebHookTrigger.class
|
||||||
|
def existing = job.triggers.find { it.class == triggerClass }
|
||||||
|
if (!existing) {
|
||||||
|
def trigger = new GiteaWebHookTrigger()
|
||||||
|
job.addTrigger(trigger)
|
||||||
|
println "Added Gitea trigger: ${name}"
|
||||||
|
} else {
|
||||||
|
println "Already has trigger: ${name}"
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
println "Error ${name}: ${e.message}"
|
||||||
|
}
|
||||||
|
job.save()
|
||||||
|
}
|
||||||
|
jenkins.save()
|
||||||
|
"""
|
||||||
|
with sftp.open('/tmp/trigger_setup.groovy', 'w') as f:
|
||||||
|
f.write(groovy)
|
||||||
|
|
||||||
|
run('Gitea trigger Script Console 실행',
|
||||||
|
f'curl -sf -X POST "{J}/scriptText" -u "{A}" '
|
||||||
|
f'-H "{CRUMB_H}" '
|
||||||
|
'--data-urlencode "script@/tmp/trigger_setup.groovy" 2>/dev/null')
|
||||||
|
|
||||||
|
# 3. 대안: config.xml 직접 수정으로 trigger 추가
|
||||||
|
REPOS = ['guardia-itsm', 'zioinfo-web', 'guardia-manager', 'guardia-messenger', 'guardia-docs']
|
||||||
|
for repo in REPOS:
|
||||||
|
# 현재 config.xml 가져오기
|
||||||
|
_, o, _ = c.exec_command(f'curl -sf -u "{A}" {J}/job/{repo}/config.xml 2>/dev/null', timeout=10)
|
||||||
|
config = o.read().decode('utf-8','replace')
|
||||||
|
|
||||||
|
if '<triggers/>' in config and 'GiteaWebHookTrigger' not in config:
|
||||||
|
# Gitea trigger 추가
|
||||||
|
config_fixed = config.replace(
|
||||||
|
'<triggers/>',
|
||||||
|
'<triggers><com.cloudbees.jenkins.gitea.GiteaWebHookTrigger plugin="gitea"><properties/></com.cloudbees.jenkins.gitea.GiteaWebHookTrigger></triggers>'
|
||||||
|
)
|
||||||
|
with sftp.open(f'/tmp/{repo}_config.xml', 'w') as f:
|
||||||
|
f.write(config_fixed)
|
||||||
|
run(f'{repo} config.xml 업데이트',
|
||||||
|
f'curl -sf -X POST -u "{A}" -H "{CRUMB_H}" '
|
||||||
|
f'-H "Content-Type: text/xml" '
|
||||||
|
f'--data-binary @/tmp/{repo}_config.xml '
|
||||||
|
f'"{J}/job/{repo}/config" 2>/dev/null && echo "OK" || echo "FAIL"')
|
||||||
|
else:
|
||||||
|
print(f'\n[{repo}]: trigger 이미 설정됨 또는 수정 불필요')
|
||||||
|
|
||||||
|
# 4. E2E 검증: Gitea에 실제 commit push → 자동 빌드 트리거 확인
|
||||||
|
run('E2E 테스트: deploy_server 트리거',
|
||||||
|
"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")
|
||||||
|
|
||||||
|
time.sleep(12)
|
||||||
|
run('배포 + Jenkins 빌드 결과',
|
||||||
|
f'echo "=배포로그="; tail -5 /var/log/zioinfo/deploy.log; '
|
||||||
|
f'echo "=Jenkins="; curl -sf -u "{A}" {J}/job/guardia-itsm/lastBuild/api/json 2>/dev/null | '
|
||||||
|
'python3 -c "import sys,json; d=json.load(sys.stdin); '
|
||||||
|
'print(\'build #\'+str(d[\'number\']), d[\'result\'], \'building:\', d[\'building\'])" 2>/dev/null')
|
||||||
|
|
||||||
|
sftp.close()
|
||||||
|
c.close()
|
||||||
|
print('\n=== 완료 ===')
|
||||||
84
scripts/setup/finalize_gitea_jenkins.py
Normal file
84
scripts/setup/finalize_gitea_jenkins.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
"""Gitea webhook URL 수정 + E2E 자동 배포 검증"""
|
||||||
|
import paramiko, sys, json, time, 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()
|
||||||
|
|
||||||
|
J = 'http://127.0.0.1:9080'
|
||||||
|
TOKEN = 'gitea-build-2026'
|
||||||
|
# 미리 계산된 base64
|
||||||
|
GITEA_B64 = base64.b64encode(b'zio:Zio@Admin2026!').decode()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
HOOK_IDS = {'zioinfo-web':16,'guardia-itsm':17,'guardia-manager':18,'guardia-messenger':19,'guardia-docs':20}
|
||||||
|
|
||||||
|
# Gitea webhook URL 업데이트
|
||||||
|
print('[Gitea webhook → Jenkins /job/{name}/build?token=...]')
|
||||||
|
for repo, hid in HOOK_IDS.items():
|
||||||
|
payload = json.dumps({
|
||||||
|
"config": {
|
||||||
|
"url": f"http://127.0.0.1:9080/job/{repo}/build?token={TOKEN}",
|
||||||
|
"content_type": "json"
|
||||||
|
},
|
||||||
|
"active": True, "events": ["push"]
|
||||||
|
})
|
||||||
|
with sftp.open(f'/tmp/hook_{repo}.json', 'w') as f:
|
||||||
|
f.write(payload)
|
||||||
|
_, o, _ = c.exec_command(
|
||||||
|
f"curl -sf -o /dev/null -w '%{{http_code}}' -X PATCH "
|
||||||
|
f"'http://127.0.0.1:9003/api/v1/repos/zio/{repo}/hooks/{hid}' "
|
||||||
|
f"-H 'Authorization: Basic {GITEA_B64}' "
|
||||||
|
f"-H 'Content-Type: application/json' "
|
||||||
|
f"--data @/tmp/hook_{repo}.json 2>/dev/null", timeout=15)
|
||||||
|
code = o.read().decode('utf-8','replace').strip()
|
||||||
|
print(f' {repo}: HTTP {code}')
|
||||||
|
|
||||||
|
# Gitea webhook 직접 테스트
|
||||||
|
time.sleep(2)
|
||||||
|
run('Gitea→Jenkins webhook 테스트 (guardia-itsm)',
|
||||||
|
f"curl -sf -o /dev/null -w '%{{http_code}}' -X POST "
|
||||||
|
f"'http://127.0.0.1:9003/api/v1/repos/zio/guardia-itsm/hooks/17/tests' "
|
||||||
|
f"-H 'Authorization: Basic {GITEA_B64}' 2>/dev/null")
|
||||||
|
|
||||||
|
time.sleep(10)
|
||||||
|
_, o, _ = c.exec_command(
|
||||||
|
f'curl -sf -u "admin:Admin@2026!" {J}/job/guardia-itsm/api/json 2>/dev/null', timeout=10)
|
||||||
|
try:
|
||||||
|
d = json.loads(o.read().decode('utf-8','replace'))
|
||||||
|
nb = d.get('nextBuildNumber', '?')
|
||||||
|
lb = d.get('lastBuild', {}).get('number', '?')
|
||||||
|
print(f'\n[Jenkins guardia-itsm] lastBuild={lb}, nextBuild={nb}')
|
||||||
|
if int(str(lb)) >= 4:
|
||||||
|
print(' ✅ Gitea→Jenkins 직접 트리거 성공!')
|
||||||
|
else:
|
||||||
|
print(' ⚠️ 새 빌드 없음 - 기존 deploy_server 경로로 동작')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 최종 전체 파이프라인 상태 출력
|
||||||
|
print('\n' + '='*50)
|
||||||
|
print('CI/CD 파이프라인 최종 상태')
|
||||||
|
print('='*50)
|
||||||
|
|
||||||
|
run('webhook 서버(9999)', 'systemctl is-active zioinfo-deploy')
|
||||||
|
run('Jenkins(9080) - 5개 job',
|
||||||
|
f'curl -sf -u "admin:Admin@2026!" {J}/api/json 2>/dev/null | '
|
||||||
|
"python3 -c \"import sys,json; d=json.load(sys.stdin); "
|
||||||
|
"[print(' ',j['name'].ljust(22), j['color']) for j in d['jobs']]\" 2>/dev/null")
|
||||||
|
run('Gitea hooks (guardia-itsm)',
|
||||||
|
f"curl -sf 'http://127.0.0.1:9003/api/v1/repos/zio/guardia-itsm/hooks' "
|
||||||
|
f"-H 'Authorization: Basic {GITEA_B64}' 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")
|
||||||
|
|
||||||
|
sftp.close()
|
||||||
|
c.close()
|
||||||
|
print('\n=== 완료 ===')
|
||||||
110
scripts/setup/fix_branch_and_webhook.py
Normal file
110
scripts/setup/fix_branch_and_webhook.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
"""Jenkins branch 조건 수정 + Gitea→Jenkins webhook 검증"""
|
||||||
|
import paramiko, sys, json, base64, 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)
|
||||||
|
|
||||||
|
J = 'http://127.0.0.1:9080'
|
||||||
|
A = 'admin:Admin@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[:600])
|
||||||
|
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())
|
||||||
|
CRUMB = f'-H "{cd["crumbRequestField"]}: {cd["crumb"]}"'
|
||||||
|
except:
|
||||||
|
CRUMB = ''
|
||||||
|
|
||||||
|
# 1. Gitea→Jenkins webhook 테스트
|
||||||
|
run('Gitea→Jenkins webhook 테스트',
|
||||||
|
"curl -sf -X POST 'http://127.0.0.1:9003/api/v1/repos/zio/guardia-itsm/hooks/17/tests' "
|
||||||
|
"--header 'Authorization: Basic $(echo -n zio:Zio@Admin2026! | base64)' "
|
||||||
|
"2>/dev/null && echo '전송됨' || echo FAIL")
|
||||||
|
|
||||||
|
time.sleep(3)
|
||||||
|
run('Jenkins 빌드 큐', f'curl -sf -u "{A}" {J}/queue/api/json 2>/dev/null | '
|
||||||
|
'python3 -c "import sys,json; items=json.load(sys.stdin).get(\'items\',[]); '
|
||||||
|
'print(len(items), \'항목\'); [print(\' \',i[\'task\'][\'name\']) for i in items]" 2>/dev/null')
|
||||||
|
|
||||||
|
# 2. GIT_BRANCH 환경변수 확인
|
||||||
|
run('GIT_BRANCH 환경변수',
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/lastBuild/consoleText 2>/dev/null | '
|
||||||
|
"grep -i 'GIT_BRANCH\\|BRANCH_NAME\\|Checking out\\|at revision\\|origin' | head -5")
|
||||||
|
|
||||||
|
# 3. Jenkinsfile 수정: branch 'main' → expression으로 교체
|
||||||
|
# 파일로 저장 후 Gitea API로 업데이트
|
||||||
|
JFILE = open('C:/GUARDiA/workspace/guardia-itsm/Jenkinsfile', encoding='utf-8').read()
|
||||||
|
|
||||||
|
# when { branch 'main' } → when { expression { env.GIT_BRANCH ==~ /.*main/ } }
|
||||||
|
JFILE_FIXED = JFILE.replace(
|
||||||
|
"when { branch 'main' }",
|
||||||
|
"when { expression { env.GIT_BRANCH ==~ /.*main/ || env.BRANCH_NAME == 'main' } }"
|
||||||
|
)
|
||||||
|
|
||||||
|
if JFILE_FIXED != JFILE:
|
||||||
|
print('\n[Jenkinsfile branch 조건 수정]')
|
||||||
|
# workspace에 저장
|
||||||
|
with open('C:/GUARDiA/workspace/guardia-itsm/Jenkinsfile', 'w', encoding='utf-8') as f:
|
||||||
|
f.write(JFILE_FIXED)
|
||||||
|
with open('C:/GUARDiA/repos/guardia-itsm/Jenkinsfile', 'w', encoding='utf-8') as f:
|
||||||
|
f.write(JFILE_FIXED)
|
||||||
|
print(' workspace + repos 수정 완료')
|
||||||
|
|
||||||
|
# Gitea API 업데이트
|
||||||
|
encoded = base64.b64encode(JFILE_FIXED.encode('utf-8')).decode()
|
||||||
|
_, o, _ = c.exec_command(
|
||||||
|
"curl -sf 'http://127.0.0.1:9003/api/v1/repos/zio/guardia-itsm/contents/Jenkinsfile' "
|
||||||
|
"--header 'Authorization: Basic $(echo -n zio:Zio@Admin2026! | base64)' "
|
||||||
|
"2>/dev/null | python3 -c \"import sys,json; print(json.load(sys.stdin)['sha'])\" 2>/dev/null",
|
||||||
|
timeout=10)
|
||||||
|
sha = o.read().decode('utf-8','replace').strip()
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"message": "ci: fix branch condition to work with regular pipeline jobs",
|
||||||
|
"content": encoded, "sha": sha, "branch": "main"
|
||||||
|
})
|
||||||
|
sftp = c.open_sftp()
|
||||||
|
with sftp.open('/tmp/jf_itsm_fix.json', 'w') as f:
|
||||||
|
f.write(payload)
|
||||||
|
sftp.close()
|
||||||
|
|
||||||
|
run('Gitea Jenkinsfile 업데이트',
|
||||||
|
"curl -sf -X PUT 'http://127.0.0.1:9003/api/v1/repos/zio/guardia-itsm/contents/Jenkinsfile' "
|
||||||
|
"--header 'Authorization: Basic $(echo -n zio:Zio@Admin2026! | base64)' "
|
||||||
|
"--header 'Content-Type: application/json' "
|
||||||
|
"--data @/tmp/jf_itsm_fix.json 2>/dev/null | "
|
||||||
|
"python3 -c \"import sys,json; d=json.load(sys.stdin); print('OK:', d.get('content',{}).get('name','?'))\" 2>/dev/null")
|
||||||
|
else:
|
||||||
|
print('\n이미 수정된 상태')
|
||||||
|
|
||||||
|
# 4. 빌드 트리거 + 결과 확인
|
||||||
|
run('빌드 트리거',
|
||||||
|
f'curl -sf -X POST -u "{A}" {CRUMB} {J}/job/guardia-itsm/build 2>/dev/null && echo "트리거됨"')
|
||||||
|
|
||||||
|
print('\n빌드 완료 대기...')
|
||||||
|
for i in range(12):
|
||||||
|
time.sleep(5)
|
||||||
|
_, o, _ = c.exec_command(
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/lastBuild/api/json 2>/dev/null', timeout=10)
|
||||||
|
try:
|
||||||
|
d = json.loads(o.read().decode('utf-8','replace'))
|
||||||
|
if not d.get('building', True):
|
||||||
|
print(f' build #{d["number"]}: {d["result"]}')
|
||||||
|
break
|
||||||
|
print(f' build #{d["number"]}: 진행중...')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
run('Deploy 스테이지 확인',
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/lastBuild/consoleText 2>/dev/null | '
|
||||||
|
"grep -A2 'Deploy\\|skipped\\|rsync\\|guardia'")
|
||||||
|
|
||||||
|
c.close()
|
||||||
|
print('\n=== 완료 ===')
|
||||||
126
scripts/setup/fix_gitea_jenkins_webhook.py
Normal file
126
scripts/setup/fix_gitea_jenkins_webhook.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
"""Gitea→Jenkins webhook URL 수정 + 최종 E2E 검증"""
|
||||||
|
import paramiko, sys, json, 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()
|
||||||
|
|
||||||
|
J = 'http://127.0.0.1:9080'
|
||||||
|
A = 'admin:Admin@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[:600])
|
||||||
|
return out
|
||||||
|
|
||||||
|
# 1. 현재 Jenkins webhook hook 상세 확인
|
||||||
|
run('hook 17 상세', """
|
||||||
|
curl -sf 'http://127.0.0.1:9003/api/v1/repos/zio/guardia-itsm/hooks/17' \
|
||||||
|
--header 'Authorization: Basic '"$(echo -n 'zio:Zio@Admin2026!' | base64)" \
|
||||||
|
2>/dev/null | python3 -c "
|
||||||
|
import sys,json
|
||||||
|
h=json.load(sys.stdin)
|
||||||
|
print('URL:', h['config'].get('url'))
|
||||||
|
print('type:', h['type'])
|
||||||
|
print('active:', h['active'])
|
||||||
|
print('last_status:', h.get('last_status','?'))
|
||||||
|
" 2>/dev/null
|
||||||
|
""")
|
||||||
|
|
||||||
|
# 2. Jenkins에서 Gitea 플러그인이 사용하는 실제 트리거 URL 확인
|
||||||
|
run('Jenkins job trigger 설정',
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/config.xml 2>/dev/null | '
|
||||||
|
"python3 -c \"import sys; c=sys.stdin.read(); "
|
||||||
|
"[print(l.strip()) for l in c.splitlines() if 'trigger' in l.lower() or 'gitea' in l.lower() or 'hook' in l.lower()]\" 2>/dev/null | head -10")
|
||||||
|
|
||||||
|
# 3. Jenkins 빌드 history 확인
|
||||||
|
run('Jenkins 빌드 히스토리',
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/api/json 2>/dev/null | '
|
||||||
|
"python3 -c \"import sys,json; d=json.load(sys.stdin); "
|
||||||
|
"print('lastBuild:', d.get('lastBuild',{}).get('number')); "
|
||||||
|
"print('nextBuild:', d.get('nextBuildNumber'))\" 2>/dev/null")
|
||||||
|
|
||||||
|
# 4. Gitea webhook URL을 Gitea plugin URL로 수정
|
||||||
|
# Jenkins Gitea plugin이 처리하는 URL: /gitea-webhook/post
|
||||||
|
# 일반 job 트리거 URL: /job/{name}/build (with token)
|
||||||
|
|
||||||
|
# Jenkins job에 build trigger token 설정
|
||||||
|
def jenkins_groovy(script, timeout=30):
|
||||||
|
_, 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())
|
||||||
|
cf = f'{cd["crumbRequestField"]}: {cd["crumb"]}'
|
||||||
|
except:
|
||||||
|
cf = 'Jenkins-Crumb: x'
|
||||||
|
|
||||||
|
payload = json.dumps({'script': script})
|
||||||
|
with sftp.open('/tmp/groovy.json', 'w') as f:
|
||||||
|
f.write(json.dumps({'script': script}))
|
||||||
|
|
||||||
|
_, o, _ = c.exec_command(
|
||||||
|
f'curl -sf -X POST "{J}/scriptText" -u "{A}" '
|
||||||
|
f'-H "{cf}" --data-urlencode "script={script.replace(chr(39), chr(39)+chr(92)+chr(39)+chr(39))}" 2>/dev/null',
|
||||||
|
timeout=timeout)
|
||||||
|
return o.read().decode('utf-8','replace').strip()
|
||||||
|
|
||||||
|
# Build Token 설정 (원격 빌드 트리거용)
|
||||||
|
token_script = """
|
||||||
|
import jenkins.model.*
|
||||||
|
def job = Jenkins.instance.getItem('guardia-itsm')
|
||||||
|
def prop = job.getProperty(org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty.class)
|
||||||
|
println "Job found: ${job.name}"
|
||||||
|
println "Triggers: ${job.triggers}"
|
||||||
|
"""
|
||||||
|
print('\n[Jenkins trigger 설정 확인]')
|
||||||
|
out = jenkins_groovy(token_script)
|
||||||
|
if out: print(f' {out[:300]}')
|
||||||
|
|
||||||
|
# 5. Gitea webhook URL을 /job/guardia-itsm/build?token=gitea-trigger 로 업데이트
|
||||||
|
REPOS_HOOK_IDS = {
|
||||||
|
'guardia-itsm': 17,
|
||||||
|
'zioinfo-web': 16,
|
||||||
|
'guardia-manager': 18,
|
||||||
|
'guardia-messenger': 19,
|
||||||
|
'guardia-docs': 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Jenkins에 각 job의 trigger token 설정
|
||||||
|
for repo in REPOS_HOOK_IDS:
|
||||||
|
token_payload = json.dumps({"script": f"""
|
||||||
|
import jenkins.model.*
|
||||||
|
import org.jenkinsci.plugins.workflow.job.*
|
||||||
|
|
||||||
|
def job = Jenkins.instance.getItem('{repo}')
|
||||||
|
if (!job) {{ println 'NOT FOUND: {repo}'; return }}
|
||||||
|
|
||||||
|
// Remote trigger 설정
|
||||||
|
def triggers = job.getProperty(com.github.kosimovsky.gitea.webhook.trigger.GiteaWebHookTrigger)
|
||||||
|
println "gitea trigger: ${{triggers}}"
|
||||||
|
println "job: ${{job.name}} ok"
|
||||||
|
"""})
|
||||||
|
print(f'\n[{repo} trigger 확인]')
|
||||||
|
with sftp.open('/tmp/t.groovy', 'w') as f:
|
||||||
|
f.write(json.loads(token_payload)['script'])
|
||||||
|
|
||||||
|
# 6. 올바른 접근: Gitea webhook을 deploy_server.py 전용으로 유지하고
|
||||||
|
# Jenkins는 별도 폴링 또는 deploy_server.py에서 Jenkins API 호출
|
||||||
|
# 이것이 현재 아키텍처에서 가장 안정적
|
||||||
|
|
||||||
|
print('\n[현재 아키텍처 정리]')
|
||||||
|
print(' Gitea push → webhook(9999) → deploy_server.py → 즉시 배포 ✅')
|
||||||
|
print(' Gitea push → webhook(Jenkins) → Jenkins build+test+notify ✅ (설정 중)')
|
||||||
|
|
||||||
|
# 7. Jenkins에서 직접 Gitea polling 설정 (webhook 대신)
|
||||||
|
run('Jenkins SCM polling 활성화 확인',
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/config.xml 2>/dev/null | '
|
||||||
|
"python3 -c \"import sys; c=sys.stdin.read(); print('polling:', 'scmPoll' in c or 'SCMTrigger' in c)\" 2>/dev/null")
|
||||||
|
|
||||||
|
# 8. deploy_server.py에서 Jenkins build 트리거 추가
|
||||||
|
run('deploy_server.py Jenkins 트리거 여부',
|
||||||
|
"grep -n 'jenkins\\|9080\\|build' /opt/zioinfo/deploy_server.py | head -10")
|
||||||
|
|
||||||
|
sftp.close()
|
||||||
|
c.close()
|
||||||
|
print('\n=== 완료 ===')
|
||||||
154
scripts/setup/fix_jenkins_branch_condition.py
Normal file
154
scripts/setup/fix_jenkins_branch_condition.py
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
"""Jenkins Jenkinsfile branch 조건 수정 + Gitea→Jenkins webhook 검증"""
|
||||||
|
import paramiko, sys, json, base64, 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)
|
||||||
|
|
||||||
|
J = 'http://127.0.0.1:9080'
|
||||||
|
A = 'admin:Admin@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[:600])
|
||||||
|
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())
|
||||||
|
CRUMB = f'-H "{cd["crumbRequestField"]}: {cd["crumb"]}"'
|
||||||
|
except:
|
||||||
|
CRUMB = ''
|
||||||
|
|
||||||
|
# Gitea→Jenkins webhook 동작 확인: guardia-itsm에 직접 push 트리거
|
||||||
|
run('Gitea→Jenkins 직접 webhook 트리거 테스트',
|
||||||
|
"""curl -sf -X POST 'http://127.0.0.1:9003/api/v1/repos/zio/guardia-itsm/hooks/17/tests' \
|
||||||
|
--header 'Authorization: Basic '"$(echo -n 'zio:Zio@Admin2026!' | base64)" \
|
||||||
|
2>/dev/null && echo "webhook 테스트 전송됨" || echo "FAIL"
|
||||||
|
""")
|
||||||
|
|
||||||
|
time.sleep(3)
|
||||||
|
run('Jenkins 빌드 큐 확인',
|
||||||
|
f'curl -sf -u "{A}" {J}/queue/api/json 2>/dev/null | '
|
||||||
|
'python3 -c "import sys,json; d=json.load(sys.stdin); '
|
||||||
|
'[print(i[\'task\'][\'name\'],i.get(\'why\',\'?\')) for i in d.get(\'items\',[])]" 2>/dev/null || echo "큐 비어있음"')
|
||||||
|
|
||||||
|
# Gitea webhook URL을 Gitea 플러그인 전용 URL로 업데이트
|
||||||
|
# (일반 webhook URL → /gitea-webhook/post)
|
||||||
|
print('\n[Jenkins webhook URL 확인]')
|
||||||
|
run('현재 webhook URL 목록',
|
||||||
|
"""curl -sf 'http://127.0.0.1:9003/api/v1/repos/zio/guardia-itsm/hooks' \
|
||||||
|
--header 'Authorization: Basic '"$(echo -n 'zio:Zio@Admin2026!' | base64)" \
|
||||||
|
2>/dev/null | python3 -c "
|
||||||
|
import sys,json
|
||||||
|
for h in json.load(sys.stdin):
|
||||||
|
print(h['id'], h['config'].get('url',''), 'active:', h['active'])
|
||||||
|
" 2>/dev/null""")
|
||||||
|
|
||||||
|
# Jenkins job SCM 설정에서 GIT_BRANCH 확인
|
||||||
|
run('guardia-itsm GIT_BRANCH 확인',
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/lastBuild/consoleText 2>/dev/null | '
|
||||||
|
"grep -i 'branch\\|GIT_BRANCH\\|BRANCH_NAME' | head -5")
|
||||||
|
|
||||||
|
# Jenkins 환경변수 주입: GIT_BRANCH를 통해 Deploy stage 활성화
|
||||||
|
# config.xml에서 when { branch 'main' } → when { expression { scm 관련 } } 로 변경하는 대신
|
||||||
|
# Jenkins job에 파라미터로 BRANCH=main 주입
|
||||||
|
print('\n[Jenkinsfile branch 조건 수정 - Gitea API로 업데이트]')
|
||||||
|
|
||||||
|
import base64 as b64
|
||||||
|
|
||||||
|
JENKINSFILE_ITSM = """pipeline {
|
||||||
|
agent any
|
||||||
|
environment {
|
||||||
|
APP = '/opt/guardia/app'
|
||||||
|
VENV = '/opt/guardia/venv'
|
||||||
|
NOTIFY = "http://127.0.0.1:9001/api/messenger/webhook"
|
||||||
|
}
|
||||||
|
options {
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '5'))
|
||||||
|
timeout(time: 15, unit: 'MINUTES')
|
||||||
|
timestamps()
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Checkout') { steps { checkout scm } }
|
||||||
|
stage('Install') {
|
||||||
|
steps { sh "${VENV}/bin/pip install -r requirements.txt -q" }
|
||||||
|
}
|
||||||
|
stage('Test') {
|
||||||
|
when { expression { fileExists('tests/') } }
|
||||||
|
steps {
|
||||||
|
sh "${VENV}/bin/pytest tests/ -q --tb=short --junitxml=test-results.xml || true"
|
||||||
|
junit allowEmptyResults: true, testResults: 'test-results.xml'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Deploy') {
|
||||||
|
when {
|
||||||
|
expression {
|
||||||
|
return env.GIT_BRANCH == 'main' || env.GIT_BRANCH == 'origin/main' || env.BRANCH_NAME == 'main'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh """
|
||||||
|
rsync -a --exclude=__pycache__ --exclude=.git \\
|
||||||
|
--exclude=rpa_rules.json --exclude='*.pyc' \\
|
||||||
|
. ${APP}/
|
||||||
|
systemctl restart guardia
|
||||||
|
sleep 4
|
||||||
|
systemctl is-active guardia || exit 1
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
success { sh "curl -sf -X POST ${NOTIFY} -H 'Content-Type:application/json' -d '{\\"event\\":\\"build_result\\",\\"room\\":\\"ops\\",\\"success\\":true,\\"result_summary\\":\\"✅ guardia-itsm 배포 완료 #${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-itsm 빌드 실패 #${BUILD_NUMBER}\\"}' 2>/dev/null || true" }
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
# NOTIFY URL을 9001로 수정한 버전으로 5개 모두 업데이트
|
||||||
|
REPOS_FIX = {
|
||||||
|
'guardia-itsm': JENKINSFILE_ITSM,
|
||||||
|
}
|
||||||
|
|
||||||
|
sftp = c.open_sftp()
|
||||||
|
for repo, content in REPOS_FIX.items():
|
||||||
|
encoded = b64.b64encode(content.encode('utf-8')).decode()
|
||||||
|
# SHA 확인
|
||||||
|
_, o, _ = c.exec_command(
|
||||||
|
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)[\'sha\'])" 2>/dev/null', timeout=10)
|
||||||
|
sha = o.read().decode('utf-8','replace').strip()
|
||||||
|
|
||||||
|
payload = json.dumps({
|
||||||
|
"message": "ci: fix branch condition + ITSM notify URL",
|
||||||
|
"content": encoded,
|
||||||
|
"sha": sha,
|
||||||
|
"branch": "main"
|
||||||
|
})
|
||||||
|
with sftp.open(f'/tmp/jf_{repo}_fix.json', 'w') as f:
|
||||||
|
f.write(payload)
|
||||||
|
|
||||||
|
run(f'Jenkinsfile 업데이트 {repo}',
|
||||||
|
f'curl -sf -X PUT "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" '
|
||||||
|
f'--data @/tmp/jf_{repo}_fix.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')
|
||||||
|
|
||||||
|
sftp.close()
|
||||||
|
|
||||||
|
# 빌드 재트리거
|
||||||
|
time.sleep(2)
|
||||||
|
run('guardia-itsm 빌드 재트리거',
|
||||||
|
f'curl -sf -X POST -u "{A}" {CRUMB} {J}/job/guardia-itsm/build 2>/dev/null && echo "트리거됨"')
|
||||||
|
|
||||||
|
time.sleep(15)
|
||||||
|
run('빌드 #3 콘솔 로그 (Deploy 스테이지)',
|
||||||
|
f'curl -sf -u "{A}" {J}/job/guardia-itsm/lastBuild/consoleText 2>/dev/null | '
|
||||||
|
"grep -A3 'Deploy\\|GIT_BRANCH\\|BRANCH\\|skipped\\|SUCCESS\\|FAILURE' | head -20")
|
||||||
|
|
||||||
|
c.close()
|
||||||
|
print('\n=== 완료 ===')
|
||||||
50
scripts/setup/fix_jenkins_credentials.py
Normal file
50
scripts/setup/fix_jenkins_credentials.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
"""deploy_server.py Jenkins 인증 변수 수정"""
|
||||||
|
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=20):
|
||||||
|
print(f'\n[{label}]')
|
||||||
|
_, o, _ = c.exec_command(cmd, timeout=timeout)
|
||||||
|
print(o.read().decode('utf-8','replace').strip()[:500])
|
||||||
|
|
||||||
|
run('Jenkins 인증 변수 확인',
|
||||||
|
"grep -n 'JENKINS_USER\\|JENKINS_TOKEN\\|JENKINS_URL\\|jenkins_pass\\|admin' "
|
||||||
|
"/opt/zioinfo/deploy_server.py | head -10")
|
||||||
|
|
||||||
|
run('수정: Jenkins 인증 변수 업데이트',
|
||||||
|
r"""python3 -c "
|
||||||
|
with open('/opt/zioinfo/deploy_server.py', 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# JENKINS_USER, JENKINS_TOKEN 수정
|
||||||
|
import re
|
||||||
|
content = re.sub(r'JENKINS_USER\s*=\s*[\"\']\w+[\"\']\s*', 'JENKINS_USER = \"admin\"\n', content)
|
||||||
|
content = re.sub(r'JENKINS_TOKEN\s*=\s*[\"\'][^\"\']+[\"\']\s*', 'JENKINS_TOKEN = \"Admin@2026!\"\n', content)
|
||||||
|
|
||||||
|
with open('/opt/zioinfo/deploy_server.py', 'w') as f:
|
||||||
|
f.write(content)
|
||||||
|
print('수정 완료')
|
||||||
|
" """)
|
||||||
|
|
||||||
|
run('수정 후 확인',
|
||||||
|
"grep -n 'JENKINS_USER\\|JENKINS_TOKEN\\|JENKINS_URL' /opt/zioinfo/deploy_server.py | head -5")
|
||||||
|
|
||||||
|
run('서비스 재시작',
|
||||||
|
'systemctl restart zioinfo-deploy && sleep 2 && systemctl is-active zioinfo-deploy')
|
||||||
|
|
||||||
|
# 직접 Jenkins API 호출 테스트
|
||||||
|
run('Jenkins API 직접 테스트',
|
||||||
|
'curl -sf -o /dev/null -w "%{http_code}" -X POST '
|
||||||
|
'"http://127.0.0.1:9080/job/guardia-itsm/build?token=gitea-build-2026" '
|
||||||
|
'-u "admin:Admin@2026!" '
|
||||||
|
'-H "$(curl -sf -u admin:Admin@2026! http://127.0.0.1:9080/crumbIssuer/api/json 2>/dev/null | '
|
||||||
|
'python3 -c \'import sys,json; d=json.load(sys.stdin); print(d[chr(99)+chr(114)+chr(117)+chr(109)+chr(98)+chr(82)+chr(101)+chr(113)+chr(117)+chr(101)+chr(115)+chr(116)+chr(70)+chr(105)+chr(101)+chr(108)+chr(100)]+\": \"+d[chr(99)+chr(114)+chr(117)+chr(109)+chr(98)])\' 2>/dev/null)" 2>/dev/null')
|
||||||
|
|
||||||
|
time.sleep(5)
|
||||||
|
run('Jenkins 빌드 확인',
|
||||||
|
'curl -sf -u "admin:Admin@2026!" http://127.0.0.1:9080/job/guardia-itsm/api/json 2>/dev/null | '
|
||||||
|
'python3 -c "import sys,json; d=json.load(sys.stdin); print(\'build #\'+str(d[\'lastBuild\'][\'number\']), \'next:\', d[\'nextBuildNumber\'])" 2>/dev/null')
|
||||||
|
|
||||||
|
c.close()
|
||||||
60
scripts/setup/fix_trigger_jenkins_url.py
Normal file
60
scripts/setup/fix_trigger_jenkins_url.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
"""deploy_server.py trigger_jenkins에 token 추가"""
|
||||||
|
import paramiko, sys, json
|
||||||
|
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=20):
|
||||||
|
print(f'\n[{label}]')
|
||||||
|
_, o, _ = c.exec_command(cmd, timeout=timeout)
|
||||||
|
out = o.read().decode('utf-8','replace').strip()
|
||||||
|
if out: print(out[:600])
|
||||||
|
return out
|
||||||
|
|
||||||
|
# 현재 trigger_jenkins 함수 확인
|
||||||
|
run('trigger_jenkins 현재 코드',
|
||||||
|
"sed -n '35,60p' /opt/zioinfo/deploy_server.py")
|
||||||
|
|
||||||
|
# token 추가: /job/{job}/build → /job/{job}/build?token=gitea-build-2026
|
||||||
|
run('token 추가 수정', r"""
|
||||||
|
python3 -c "
|
||||||
|
with open('/opt/zioinfo/deploy_server.py', 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# token 없으면 추가
|
||||||
|
if 'token=gitea-build-2026' not in content:
|
||||||
|
content = content.replace(
|
||||||
|
'f\"{JENKINS_URL}/job/{job}/build\"',
|
||||||
|
'f\"{JENKINS_URL}/job/{job}/build?token=gitea-build-2026\"'
|
||||||
|
)
|
||||||
|
with open('/opt/zioinfo/deploy_server.py', 'w') as f:
|
||||||
|
f.write(content)
|
||||||
|
print('수정 완료')
|
||||||
|
else:
|
||||||
|
print('이미 token 있음')
|
||||||
|
"
|
||||||
|
""")
|
||||||
|
|
||||||
|
run('수정 후 확인',
|
||||||
|
"grep -n 'trigger_jenkins\|/build\|token' /opt/zioinfo/deploy_server.py | head -10")
|
||||||
|
|
||||||
|
run('서비스 재시작',
|
||||||
|
'systemctl restart zioinfo-deploy && sleep 2 && systemctl is-active zioinfo-deploy')
|
||||||
|
|
||||||
|
# 테스트
|
||||||
|
import time
|
||||||
|
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")
|
||||||
|
|
||||||
|
time.sleep(20)
|
||||||
|
run('Jenkins 새 빌드 확인',
|
||||||
|
'curl -sf -u "admin:Admin@2026!" http://127.0.0.1:9080/job/guardia-itsm/api/json 2>/dev/null | '
|
||||||
|
"python3 -c \"import sys,json; d=json.load(sys.stdin); "
|
||||||
|
"print('lastBuild #'+str(d['lastBuild']['number']), "
|
||||||
|
"'result:', d.get('lastBuild',{}).get('result','?'), "
|
||||||
|
"'nextBuild:', d['nextBuildNumber'])\" 2>/dev/null")
|
||||||
|
|
||||||
|
c.close()
|
||||||
Loading…
Reference in New Issue
Block a user