feat(cicd): sync workspace to repos, fix git ownership and pull strategy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5561d0d050
commit
f4f5abd65b
116
scripts/push/sync_workspace_to_repos.py
Normal file
116
scripts/push/sync_workspace_to_repos.py
Normal file
@ -0,0 +1,116 @@
|
||||
"""workspace/ → repos/ 동기화 후 Gitea push"""
|
||||
import subprocess, shutil, os, sys, paramiko, json, base64
|
||||
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
||||
|
||||
EXCLUDE = {'.git', '__pycache__', 'node_modules', '.expo',
|
||||
'dist', 'build', 'target', '*.db', '*.pyc', '.env'}
|
||||
|
||||
def should_skip(name):
|
||||
if name.startswith('.'): return False # .env 등 포함
|
||||
for pat in EXCLUDE:
|
||||
if pat.startswith('*'):
|
||||
if name.endswith(pat[1:]): return True
|
||||
elif name == pat:
|
||||
return True
|
||||
return False
|
||||
|
||||
def sync_dir(src, dst):
|
||||
"""src → dst 파일 동기화 (dst의 .git 보존)"""
|
||||
added, updated = 0, 0
|
||||
# dst에 없는 파일 / 오래된 파일 복사
|
||||
for root, dirs, files in os.walk(src):
|
||||
# 제외 폴더
|
||||
dirs[:] = [d for d in dirs if d not in EXCLUDE and not d.endswith('.egg-info')]
|
||||
rel_root = os.path.relpath(root, src)
|
||||
dst_root = os.path.join(dst, rel_root) if rel_root != '.' else dst
|
||||
os.makedirs(dst_root, exist_ok=True)
|
||||
for f in files:
|
||||
if f.endswith(('.pyc', '.db', '.db-journal')): continue
|
||||
s = os.path.join(root, f)
|
||||
d = os.path.join(dst_root, f)
|
||||
try:
|
||||
if not os.path.exists(d):
|
||||
shutil.copy2(s, d); added += 1
|
||||
elif os.path.getmtime(s) > os.path.getmtime(d):
|
||||
shutil.copy2(s, d); updated += 1
|
||||
except: pass
|
||||
return added, updated
|
||||
|
||||
def git(path, *args, check=False):
|
||||
r = subprocess.run(['git', '-C', path] + list(args),
|
||||
capture_output=True, text=True,
|
||||
encoding='utf-8', errors='replace')
|
||||
return r.stdout.strip(), r.returncode
|
||||
|
||||
REPOS = ['guardia-itsm', 'guardia-manager', 'guardia-docs', 'zioinfo-web', 'guardia-messenger']
|
||||
BASE_W = 'C:/GUARDiA/workspace'
|
||||
BASE_R = 'C:/GUARDiA/repos'
|
||||
|
||||
# Gitea API 업로드용 클라이언트
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect('101.79.17.164', username='root', password='1q2w3e!Q', timeout=15)
|
||||
|
||||
def gitea_push_bundle(repo):
|
||||
"""bundle → 서버 → Gitea push"""
|
||||
local = f'{BASE_R}/{repo}'
|
||||
bundle = f'{BASE_R}/{repo}.bundle'
|
||||
sftp = ssh.open_sftp()
|
||||
print(f' bundle 생성 중...')
|
||||
subprocess.run(['git', '-C', local, 'bundle', 'create', bundle, '--all'],
|
||||
capture_output=True, timeout=120)
|
||||
size = os.path.getsize(bundle) // 1024
|
||||
print(f' 크기: {size}KB → 서버 전송...')
|
||||
sftp.put(bundle, f'/tmp/{repo}.bundle')
|
||||
sftp.close()
|
||||
os.remove(bundle)
|
||||
|
||||
def srv(cmd, t=60):
|
||||
_, o, e = ssh.exec_command(cmd, timeout=t)
|
||||
return o.read().decode('utf-8','replace').strip()
|
||||
|
||||
result = srv(f"""
|
||||
rm -rf /tmp/{repo}_push
|
||||
git clone /tmp/{repo}.bundle /tmp/{repo}_push 2>/dev/null
|
||||
cd /tmp/{repo}_push
|
||||
git remote set-url origin 'http://zio:Zio%40Admin2026%21@127.0.0.1:9003/zio/{repo}.git'
|
||||
git push origin main --force 2>&1 | tail -3
|
||||
rm -rf /tmp/{repo}_push /tmp/{repo}.bundle
|
||||
echo "DONE"
|
||||
""", t=120)
|
||||
return 'DONE' in result, result
|
||||
|
||||
print('=== workspace → repos 동기화 ===\n')
|
||||
for repo in REPOS:
|
||||
src = f'{BASE_W}/{repo}'
|
||||
dst = f'{BASE_R}/{repo}'
|
||||
if not os.path.isdir(src):
|
||||
print(f'[{repo}] workspace 없음, skip')
|
||||
continue
|
||||
|
||||
print(f'[{repo}]')
|
||||
added, updated = sync_dir(src, dst)
|
||||
print(f' sync: +{added} 추가, {updated} 갱신')
|
||||
|
||||
# git 상태 확인
|
||||
out, _ = git(dst, 'status', '--short')
|
||||
if not out:
|
||||
print(' 변경 없음')
|
||||
continue
|
||||
|
||||
changed = len(out.splitlines())
|
||||
print(f' 변경 파일: {changed}개')
|
||||
|
||||
git(dst, 'add', '-A')
|
||||
msg = f'sync: update from workspace (latest ITSM/CICD/DR changes)'
|
||||
git(dst, 'commit', '-m', msg)
|
||||
|
||||
print(' Gitea push 중...')
|
||||
ok, log = gitea_push_bundle(repo)
|
||||
if ok:
|
||||
print(f' ✅ push 완료')
|
||||
else:
|
||||
print(f' ❌ push 실패: {log[:200]}')
|
||||
|
||||
ssh.close()
|
||||
print('\n=== 동기화 완료 ===')
|
||||
71
scripts/setup/fix_deploy_pull_strategy.py
Normal file
71
scripts/setup/fix_deploy_pull_strategy.py
Normal file
@ -0,0 +1,71 @@
|
||||
"""deploy_server.py git pull → fetch+reset 방식으로 변경 + 서버 클론 강제 리셋"""
|
||||
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=40):
|
||||
print(f'\n[{label}]')
|
||||
_, o, e = client.exec_command(cmd, timeout=timeout)
|
||||
out = o.read().decode('utf-8','replace').strip()
|
||||
if out: print(out[:500])
|
||||
return out
|
||||
|
||||
GITEA = 'http://zio:Zio%40Admin2026%21@127.0.0.1:9003/zio'
|
||||
|
||||
# 1. 서버 src 디렉토리 강제 리셋 (diverge 해결)
|
||||
for path, repo in [('/opt/guardia/src', 'guardia-itsm'), ('/opt/zioinfo/src', 'zioinfo-web')]:
|
||||
run(f'{repo} 강제 리셋', f"""
|
||||
git config --global --add safe.directory {path} 2>/dev/null
|
||||
git -C {path} remote set-url origin '{GITEA}/{repo}.git'
|
||||
git -C {path} fetch origin main
|
||||
git -C {path} reset --hard origin/main
|
||||
echo "리셋 완료: $(git -C {path} log -1 --oneline)"
|
||||
""")
|
||||
|
||||
# 2. deploy_server.py git pull → fetch+reset 방식으로 교체
|
||||
run('deploy_server.py 수정 확인', "grep -n 'git pull\|git -C\|pull origin' /opt/zioinfo/deploy_server.py | head -15")
|
||||
|
||||
run('git pull 명령 교체', r"""
|
||||
# ["git", "-C", SRC, "pull", "origin", "main"] → fetch + reset
|
||||
python3 -c "
|
||||
import re
|
||||
with open('/opt/zioinfo/deploy_server.py', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# git pull 단순 명령 교체
|
||||
content = content.replace(
|
||||
'[\"git\", \"-C\", SRC, \"pull\", \"origin\", \"main\"]',
|
||||
'[\"bash\", \"-c\", f\"git -C {SRC} fetch origin main && git -C {SRC} reset --hard origin/main\"]'
|
||||
)
|
||||
|
||||
# bash git pull 교체
|
||||
content = content.replace(
|
||||
'\"[ -d {SRC}/.git ] && git -C {SRC} pull origin main\"',
|
||||
'\"[ -d {SRC}/.git ] && git -C {SRC} fetch origin main && git -C {SRC} reset --hard origin/main\"'
|
||||
)
|
||||
|
||||
with open('/opt/zioinfo/deploy_server.py', 'w') as f:
|
||||
f.write(content)
|
||||
print('수정 완료')
|
||||
"
|
||||
""")
|
||||
|
||||
run('수정 결과 확인', "grep -n 'git.*pull\|fetch\|reset' /opt/zioinfo/deploy_server.py | head -10")
|
||||
|
||||
# 3. 서비스 재시작
|
||||
run('서비스 재시작', 'systemctl restart zioinfo-deploy && sleep 2 && systemctl is-active zioinfo-deploy')
|
||||
|
||||
# 4. 배포 재트리거
|
||||
import time
|
||||
for repo in ['guardia-itsm', 'zioinfo-web']:
|
||||
run(f'{repo} 배포 트리거',
|
||||
f'curl -sf -X POST http://localhost:9999 '
|
||||
f'-H "Content-Type: application/json" -H "X-Gitea-Event: push" '
|
||||
f'-d \'{{"repository":{{"name":"{repo}"}},"ref":"refs/heads/main"}}\' 2>/dev/null')
|
||||
|
||||
time.sleep(12)
|
||||
run('최종 배포 로그', 'tail -20 /var/log/zioinfo/deploy.log 2>/dev/null')
|
||||
|
||||
client.close()
|
||||
48
scripts/setup/fix_git_ownership.py
Normal file
48
scripts/setup/fix_git_ownership.py
Normal file
@ -0,0 +1,48 @@
|
||||
"""git safe.directory + remote 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[:400])
|
||||
return out
|
||||
|
||||
GITEA = 'http://zio:Zio%40Admin2026%21@127.0.0.1:9003/zio'
|
||||
|
||||
SRC_DIRS = {
|
||||
'/opt/guardia/src': f'{GITEA}/guardia-itsm.git',
|
||||
'/opt/zioinfo/src': f'{GITEA}/zioinfo-web.git',
|
||||
'/opt/manager/src': f'{GITEA}/guardia-manager.git',
|
||||
'/opt/guardia-docs/src': f'{GITEA}/guardia-docs.git',
|
||||
}
|
||||
|
||||
# safe.directory 등록 + ownership 수정 + remote update
|
||||
for path, url in SRC_DIRS.items():
|
||||
run(f'fix {path}', f"""
|
||||
git config --global --add safe.directory {path} 2>/dev/null
|
||||
chown -R root:root {path} 2>/dev/null
|
||||
if [ -d {path}/.git ]; then
|
||||
git -C {path} remote set-url origin '{url}'
|
||||
git -C {path} pull origin main 2>&1 | tail -3
|
||||
else
|
||||
echo "no clone yet"
|
||||
fi
|
||||
""", timeout=30)
|
||||
|
||||
# 배포 트리거
|
||||
import time
|
||||
for repo in ['guardia-itsm', 'zioinfo-web']:
|
||||
run(f'{repo} 배포 트리거',
|
||||
f'curl -sf -X POST http://localhost:9999 '
|
||||
f'-H "Content-Type: application/json" '
|
||||
f'-H "X-Gitea-Event: push" '
|
||||
f'-d \'{{"repository":{{"name":"{repo}"}},"ref":"refs/heads/main"}}\' 2>/dev/null')
|
||||
|
||||
time.sleep(12)
|
||||
run('최종 배포 로그', 'tail -20 /var/log/zioinfo/deploy.log 2>/dev/null')
|
||||
client.close()
|
||||
Loading…
Reference in New Issue
Block a user